/*
 * FreeRTOS+FAT V2.3.3
 * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * https://www.FreeRTOS.org
 * https://github.com/FreeRTOS
 *
 */

/**
 *	@file		ff_dir.c
 *	@ingroup	DIR
 *
 *	@defgroup	DIR Handles Directory Traversal
 *	@brief		Handles DIR access and traversal.
 *
 *	Provides FindFirst() and FindNext() Interfaces
 **/

#include "ff_headers.h"

#include <stdio.h>

#if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
    #include <wchar.h>
#endif

#if defined( WIN32 )
    #define wcsicmp    _wcsicmp
#else
    #define wcsicmp    wcscasecmp
    #include <ctype.h>
#endif

/* Calculate a simple LFN checksum. */
static uint8_t FF_CreateChkSum( const uint8_t * pa_pShortName );

static BaseType_t FF_ShortNameExists( FF_IOManager_t * pxIOManager,
                                      uint32_t ulDirCluster,
                                      char * pcShortName,
                                      FF_Error_t * pxError );

#if ( ffconfigSHORTNAME_CASE != 0 )

/* For short-name entries, NT/XP etc store case information in byte 0x0c
 * Use this to show proper case of "README.txt" or "source.H".
 */
    #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
        static void FF_CaseShortName( FF_T_WCHAR * pcName,
                                      uint8_t attrib );
    #else
        static void FF_CaseShortName( char * pcName,
                                      uint8_t attrib );
    #endif
#endif /* ffconfigSHORTNAME_CASE */

#if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )

/* For unicode, the short name can be expanded to wchar
 * by inserting zero's.
 */
    static void FF_ShortNameExpand( FF_T_WCHAR * );

#endif

/*
 * Transform a name as stored on disk "README__TXT"
 * to a nul-terminated string: "README.TXT", "FILE001".
 * A dot is only added if an extension is present.
 */
static void FF_ProcessShortName( char * pcName );

#if ( ffconfigTIME_SUPPORT != 0 )
    static void FF_PlaceTime( uint8_t * pucEntryBuffer,
                              uint32_t Offset,
                              FF_SystemTime_t * pxTime );
    static void FF_PlaceDate( uint8_t * pucEntryBuffer,
                              uint32_t Offset,
                              FF_SystemTime_t * pxTime );
    static void FF_GetTime( FF_SystemTime_t * pxTime,
                            const uint8_t * pucEntryBuffer,
                            uint32_t ulOffset );
    static void FF_GetDate( FF_SystemTime_t * pxTime,
                            const uint8_t * pucEntryBuffer,
                            uint32_t ulOffset );
#endif /* ffconfigTIME_SUPPORT */
static FF_Error_t FF_Traverse( FF_IOManager_t * pxIOManager,
                               uint32_t ulEntry,
                               FF_FetchContext_t * pxContext );
static int32_t FF_FindFreeDirent( FF_IOManager_t * pxIOManager,
                                  FF_FindParams_t * pxFindParams,
                                  uint16_t usSequential );

#if ( ffconfigLFN_SUPPORT != 0 )
    static int8_t FF_CreateLFNEntry( uint8_t * pucEntryBuffer,
                                     uint8_t * pcName,
                                     UBaseType_t uxNameLength,
                                     UBaseType_t uxLFN,
                                     uint8_t ucCheckSum );
#endif /* ffconfigLFN_SUPPORT */

static BaseType_t FF_ValidShortChar( char cChar );

#if ( ffconfigLFN_SUPPORT != 0 )
    #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
        static FF_Error_t FF_CreateLFNs( FF_IOManager_t * pxIOManager,
                                         uint32_t ulDirCluster,
                                         FF_T_WCHAR * pcName,
                                         uint8_t ucCheckSum,
                                         uint16_t usEntry );
    #else
        static FF_Error_t FF_CreateLFNs( FF_IOManager_t * pxIOManager,
                                         uint32_t ulDirCluster,
                                         char * pcName,
                                         uint8_t ucCheckSum,
                                         uint16_t usEntry );
    #endif /* if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) */
#endif /* if ( ffconfigLFN_SUPPORT != 0 ) */

#if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
    static void FF_MakeNameCompliant( FF_T_WCHAR * pcName );
#else
    static void FF_MakeNameCompliant( char * pcName );
#endif

#if ( FF_NOSTRCASECMP == 0 )
    static portINLINE unsigned char prvToLower( unsigned char c )
    {
        unsigned char cReturnChar;

        if( ( c >= 'A' ) && ( c <= 'Z' ) )
        {
            cReturnChar = c + 0x20;
        }
        else
        {
            cReturnChar = c;
        }

        return cReturnChar;
    }

    int strcasecmp( const char * pcString1,
                    const char * pcString2 )
    {
        unsigned char c1, c2;

        do
        {
            c1 = ( unsigned char ) *pcString1++;
            c2 = ( unsigned char ) *pcString2++;
            c1 = prvToLower( c1 );
            c2 = prvToLower( c2 );
        }
        while( ( c1 == c2 ) && ( c1 != '\0' ) );

        return ( int ) c1 - c2;
    } /* strcasecmp() */
#endif /* if ( FF_NOSTRCASECMP == 0 ) */
/*-----------------------------------------------------------*/

static uint8_t FF_CreateChkSum( const uint8_t * pa_pShortName )
{
    uint8_t cNameLen;
    uint8_t ChkSum = 0;

    for( cNameLen = 11; cNameLen != 0; cNameLen-- )
    {
        ChkSum = ( uint8_t )
                 ( ( ( ChkSum & 1 ) ? 0x80 : 0 ) + ( ChkSum >> 1 ) + *( pa_pShortName++ ) );
    }

    return ChkSum;
} /* FF_CreateChkSum() */
/*-----------------------------------------------------------*/

/* _HT_ Does not need a wchar version because a short name is treated  a normal string of bytes */
static BaseType_t FF_ShortNameExists( FF_IOManager_t * pxIOManager,
                                      uint32_t ulDirCluster,
                                      char * pcShortName,
                                      FF_Error_t * pxError )
{
    BaseType_t xIndex;
    const uint8_t * pucEntryBuffer = NULL; /* initialisation not necessary, just for the compiler */
    uint8_t ucAttrib;
    FF_FetchContext_t xFetchContext;
    char pcMyShortName[ FF_SIZEOF_DIRECTORY_ENTRY ];
    BaseType_t xResult = -1;

    #if ( ffconfigHASH_CACHE != 0 )
        uint32_t ulHash;
    #endif

    *pxError = FF_ERR_NONE;

    #if ( ffconfigHASH_CACHE != 0 )
    {
        if( !FF_DirHashed( pxIOManager, ulDirCluster ) )
        {
            /* Hash the directory. */
            FF_HashDir( pxIOManager, ulDirCluster );
        }

        #if ffconfigHASH_FUNCTION == CRC16
        {
            ulHash = ( uint32_t ) FF_GetCRC16( ( uint8_t * ) pcShortName, ( uint32_t ) strlen( pcShortName ) );
        }
        #else /* ffconfigHASH_FUNCTION == CRC8 */
        {
            ulHash = ( uint32_t ) FF_GetCRC8( ( uint8_t * ) pcShortName, ( uint32_t ) strlen( pcShortName ) );
        }
        #endif
        {
            /* FF_CheckDirentHash result: 0 not found, 1 found, -1 directory not hashed */
            xResult = FF_CheckDirentHash( pxIOManager, ulDirCluster, ulHash );
        }
    }
    #endif /* if ( ffconfigHASH_CACHE != 0 ) */

    if( xResult < 0 )
    {
        xResult = pdFALSE;
        *pxError = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext );

        if( FF_isERR( *pxError ) == pdFALSE )
        {
            for( xIndex = 0; xIndex < FF_MAX_ENTRIES_PER_DIRECTORY; xIndex++ )
            {
                /* Call FF_FetchEntryWithContext only once for every block (usually 512 bytes) */
                if( ( xIndex == 0 ) ||
                    ( pucEntryBuffer >= xFetchContext.pxBuffer->pucBuffer + ( pxIOManager->usSectorSize - FF_SIZEOF_DIRECTORY_ENTRY ) ) )
                {
                    *pxError = FF_FetchEntryWithContext( pxIOManager, ( uint32_t ) xIndex, &xFetchContext, NULL );

                    if( FF_isERR( *pxError ) )
                    {
                        break;
                    }

                    pucEntryBuffer = xFetchContext.pxBuffer->pucBuffer;
                }
                else
                {
                    /* Advance 32 bytes to get the next directory entry. */
                    pucEntryBuffer += FF_SIZEOF_DIRECTORY_ENTRY;
                }

                if( FF_isEndOfDir( pucEntryBuffer ) )
                {
                    break;
                }

                if( FF_isDeleted( pucEntryBuffer ) == pdFALSE )
                {
                    ucAttrib = FF_getChar( pucEntryBuffer, FF_FAT_DIRENT_ATTRIB );

                    if( ( ucAttrib & FF_FAT_ATTR_LFN ) != FF_FAT_ATTR_LFN )
                    {
                        memcpy( pcMyShortName, pucEntryBuffer, sizeof( pcMyShortName ) );
                        FF_ProcessShortName( pcMyShortName );

                        if( strcmp( ( const char * ) pcShortName, ( const char * ) pcMyShortName ) == 0 )
                        {
                            xResult = pdTRUE;
                            break;
                        }
                    }
                }
            } /* for ( xIndex = 0; xIndex < FF_MAX_ENTRIES_PER_DIRECTORY; xIndex++ ) */
        }

        *pxError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );
    }

    return xResult;
} /* FF_ShortNameExists() */
/*-----------------------------------------------------------*/

/* _HT_ When adding many files to a single directory, FF_FindEntryInDir was sometimes */
/* _HT_ called 3 times before inserting a single file. With these changes it is called one time only */
/* _HT_ pxFindParams caches some information: */
/* _HT_ 1: the first free entry 2: whether the short-name version already exists */
/* *INDENT-OFF* */
#if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
    uint32_t FF_FindEntryInDir( FF_IOManager_t * pxIOManager,
                                FF_FindParams_t * pxFindParams,
                                const FF_T_WCHAR * pcName,
                                uint8_t pa_Attrib,
                                FF_DirEnt_t * pxDirEntry,
                                FF_Error_t * pxError )
#else
    uint32_t FF_FindEntryInDir( FF_IOManager_t * pxIOManager,
                                FF_FindParams_t * pxFindParams,
                                const char * pcName,
                                uint8_t pa_Attrib,
                                FF_DirEnt_t * pxDirEntry,
                                FF_Error_t * pxError )
#endif /* if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) */
/* *INDENT-ON* */
{
    FF_FetchContext_t xFetchContext;
/* const pointer to read from pBuffer */
    const uint8_t * src = NULL;

/* As we're walking through a directory, we might as well
 * find the first free entry to help FF_FindFreeDirent( )
 * The result will be stored in 'pxFindParams->lFreeEntry' */
    BaseType_t entriesNeeded;
    BaseType_t freeCount = 0;
    FF_Error_t xError;

/* If the file name fits into a short file name
 * then the existence of that short file name will be checked as well. */
    BaseType_t testShortname;
    uint32_t xResult = 0ul;

    #if ( ffconfigUNICODE_UTF8_SUPPORT == 1 )
        int32_t utf8Error;
    #endif

    #if ( ffconfigLFN_SUPPORT != 0 )
        #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
            FF_T_WCHAR * pcCurPtr; /* Pointer to store a LFN. */
            FF_T_WCHAR * pcLastPtr = pxDirEntry->pcFileName + sizeof( pxDirEntry->pcFileName );
        #else
            char * pcCurPtr; /* Pointer to store a LFN. */
            char * pcLastPtr = pxDirEntry->pcFileName + sizeof( pxDirEntry->pcFileName );
        #endif /* ffconfigUNICODE_UTF16_SUPPORT */

        uint16_t lfnItem = 0;
        uint8_t ucCheckSum = 0;
        BaseType_t xLFNCount = 0;
        BaseType_t xLFNTotal = 0;
        uint8_t lastAttrib;

        BaseType_t xIndex;
    #endif /* ffconfigLFN_SUPPORT */

    #if ( ffconfigLFN_SUPPORT != 0 )
    {
        #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
            BaseType_t NameLen = ( BaseType_t ) wcslen( ( const char * ) pcName );
        #else
            BaseType_t NameLen = ( BaseType_t ) strlen( ( const char * ) pcName );
        #endif
        /* Find enough places for the LFNs and the ShortName. */
        entriesNeeded = ( uint8_t ) ( ( NameLen + 12 ) / 13 ) + 1;
    }
    #else
    {
        entriesNeeded = 1;
    }
    #endif /* ffconfigLFN_SUPPORT */

    if( ( pxFindParams->ulFlags & FIND_FLAG_FITS_SHORT_OK ) == FIND_FLAG_FITS_SHORT_OK )
    {
        testShortname = pdTRUE;
    }
    else
    {
        testShortname = pdFALSE;
    }

    pxDirEntry->ucAttrib = 0;

    if( ( pxFindParams->ulFlags & FIND_FLAG_CREATE_FLAG ) != 0 )
    {
        /* A file is to be created: keep track of the first free entry big enough
         * to hold this file name. */
        pxFindParams->lFreeEntry = -1;
    }
    else
    {
        pxFindParams->lFreeEntry = 0;
    }

    xError = FF_InitEntryFetch( pxIOManager, pxFindParams->ulDirCluster, &xFetchContext );

    if( FF_isERR( xError ) == pdFALSE )
    {
        for( pxDirEntry->usCurrentItem = 0; pxDirEntry->usCurrentItem < FF_MAX_ENTRIES_PER_DIRECTORY; pxDirEntry->usCurrentItem++ )
        {
            if( ( src == NULL ) ||
                ( src >= xFetchContext.pxBuffer->pucBuffer + ( pxIOManager->usSectorSize - FF_SIZEOF_DIRECTORY_ENTRY ) ) )
            {
                xError = FF_FetchEntryWithContext( pxIOManager, pxDirEntry->usCurrentItem, &xFetchContext, NULL );

                if( FF_isERR( xError ) != pdFALSE )
                {
                    break;
                }

                src = xFetchContext.pxBuffer->pucBuffer;
            }
            else
            {
                /* Advance 32 bytes. */
                src += FF_SIZEOF_DIRECTORY_ENTRY;
            }

            if( FF_isEndOfDir( src ) )
            {
                /* 0x00 end-of-dir. */
                break;
            }

            if( FF_isDeleted( src ) )
            {
                /* Entry not used or deleted. */
                pxDirEntry->ucAttrib = 0;

                if( ( pxFindParams->lFreeEntry < 0 ) && ( ++freeCount == entriesNeeded ) )
                {
                    /* Remember the beginning entry in the sequential sequence. */
                    pxFindParams->lFreeEntry = ( pxDirEntry->usCurrentItem - ( int32_t ) ( entriesNeeded - 1 ) );
                }

                continue;
            }

            /* The current entry is in use, so reset the free-entry-counter */
            freeCount = 0;
            #if ( ffconfigLFN_SUPPORT != 0 )
            {
                lastAttrib = pxDirEntry->ucAttrib;
            }
            #endif

            pxDirEntry->ucAttrib = FF_getChar( src, FF_FAT_DIRENT_ATTRIB );

            if( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_LFN ) == FF_FAT_ATTR_LFN )
            {
                /* LFN Processing. */
                #if ( ffconfigLFN_SUPPORT != 0 )
                {
                    if( ( xLFNCount == 0 ) || ( ( lastAttrib & FF_FAT_ATTR_LFN ) != FF_FAT_ATTR_LFN ) )
                    {
                        xLFNTotal = xLFNCount = ( BaseType_t ) ( src[ 0 ] & ~0x40 );
                        lfnItem = pxDirEntry->usCurrentItem;
                        ucCheckSum = FF_getChar( src, FF_FAT_LFN_CHECKSUM );
                        pcLastPtr[ -1 ] = '\0';
                    }

                    if( xLFNCount != 0 )
                    {
                        xLFNCount--;
                        pcCurPtr = pxDirEntry->pcFileName + ( xLFNCount * 13 );

                        /*
                         *  This section needs to extract the name and do the comparison
                         *  dependent on UNICODE settings in the FreeRTOSFATConfig.h file.
                         */
                        #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
                        {
                            /* Add UTF-16 Routine here. */
                            /* Copy first 5 UTF-16 chars ( 10 bytes ). */
                            memcpy( pcCurPtr, &src[ FF_FAT_LFN_NAME_1 ], 10 );
                            /* Increment Filename pointer 5 utf16 chars. */
                            pcCurPtr += 5;

                            /* Copy next 6 chars ( 12 bytes ). */
                            memcpy( pcCurPtr, &src[ FF_FAT_LFN_NAME_2 ], 12 );
                            pcCurPtr += 6;

                            /* You're getting the idea by now! */
                            memcpy( pcCurPtr, &src[ FF_FAT_LFN_NAME_3 ], 4 );
                            pcCurPtr += 2;
                        } /* ffconfigUNICODE_UTF16_SUPPORT */
                        #elif ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
                        {
                            /* UTF-8 Routine here. */
                            for( xIndex = 0; ( xIndex < 5 ) && ( pcCurPtr < pcLastPtr ); xIndex++ )
                            {
                                /* Was there a surrogate sequence? -- Add handling here. */
                                utf8Error = FF_Utf16ctoUtf8c( ( uint8_t * ) pcCurPtr, ( uint16_t * ) &src[ FF_FAT_LFN_NAME_1 + ( 2 * xIndex ) ], pcLastPtr - pcCurPtr );

                                if( utf8Error > 0 )
                                {
                                    pcCurPtr += utf8Error;
                                }
                                else
                                {
                                    if( FF_GETERROR( utf8Error ) == FF_ERR_UNICODE_INVALID_SEQUENCE )
                                    {
                                        /* Handle potential surrogate sequence across entries. */
                                    }
                                }
                            }

                            for( xIndex = 0; xIndex < 6 && pcCurPtr < pcLastPtr; xIndex++ )
                            {
                                /* Was there a surrogate sequence? -- To add handling here. */
                                utf8Error = FF_Utf16ctoUtf8c( ( uint8_t * ) pcCurPtr, ( uint16_t * ) &src[ FF_FAT_LFN_NAME_2 + ( 2 * xIndex ) ], pcLastPtr - pcCurPtr );

                                if( utf8Error > 0 )
                                {
                                    pcCurPtr += utf8Error;
                                }
                                else
                                {
                                    if( FF_GETERROR( utf8Error ) == FF_ERR_UNICODE_INVALID_SEQUENCE )
                                    {
                                        /* Handle potential surrogate sequence across entries. */
                                    }
                                }
                            }

                            for( xIndex = 0; xIndex < 2 && pcCurPtr < pcLastPtr; xIndex++ )
                            {
                                /* Was there a surrogate sequence? -- To add handling here. */
                                utf8Error = FF_Utf16ctoUtf8c( ( uint8_t * ) pcCurPtr, ( uint16_t * ) &src[ FF_FAT_LFN_NAME_3 + ( 2 * xIndex ) ], pcLastPtr - pcCurPtr );

                                if( utf8Error > 0 )
                                {
                                    pcCurPtr += utf8Error;
                                }
                                else
                                {
                                    if( FF_GETERROR( utf8Error ) == FF_ERR_UNICODE_INVALID_SEQUENCE )
                                    {
                                        /* Handle potential surrogate sequence across entries. */
                                    }
                                }
                            }
                        } /* ffconfigUNICODE_UTF8_SUPPORT */
                        #else /* if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) */
                        { /* use ASCII notation. */
                            for( xIndex = 0; ( xIndex < 10 ) && ( pcCurPtr < pcLastPtr ); xIndex += 2 )
                            {
                                *( pcCurPtr++ ) = ( char ) src[ FF_FAT_LFN_NAME_1 + xIndex ];
                            }

                            for( xIndex = 0; ( xIndex < 12 ) && ( pcCurPtr < pcLastPtr ); xIndex += 2 )
                            {
                                *( pcCurPtr++ ) = ( char ) src[ FF_FAT_LFN_NAME_2 + xIndex ];
                            }

                            for( xIndex = 0; ( xIndex < 4 ) && ( pcCurPtr < pcLastPtr ); xIndex += 2 )
                            {
                                *( pcCurPtr++ ) = ( char ) src[ FF_FAT_LFN_NAME_3 + xIndex ];
                            }
                        }
                        #endif /* ( ffconfigUNICODE_UTF16_SUPPORT == 0 ) && !( ffconfigUNICODE_UTF8_SUPPORT == 0 ) */

                        if( ( xLFNCount == xLFNTotal - 1 ) && ( pcCurPtr < pcLastPtr ) )
                        {
                            *pcCurPtr = '\0'; /* Important when name len is multiple of 13. */
                        }
                    } /* if( xLFNCount ) */
                }
                #endif /* ffconfigLFN_SUPPORT */
                continue;
            }

            if( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_VOLID ) == FF_FAT_ATTR_VOLID )
            {
                #if ( ffconfigLFN_SUPPORT != 0 )
                {
                    xLFNTotal = 0;
                }
                #endif /* ffconfigLFN_SUPPORT */
                continue;
            }

            #if ( ffconfigLFN_SUPPORT != 0 )
                if( ( xLFNTotal == 0 ) || ( ucCheckSum != FF_CreateChkSum( src ) ) )
            #endif /* ffconfigLFN_SUPPORT */
            {
                /* This entry has only a short name, or the checksum isn't correct
                 * Use the short name for comparison */
                memcpy( pxDirEntry->pcFileName, src, 11 );
                FF_ProcessShortName( ( char * ) pxDirEntry->pcFileName );
                #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
                {
                    /* FileName now contains a 8-bit short name
                     * Expand it to a FF_T_WCHAR string. */
                    FF_ShortNameExpand( pxDirEntry->pcFileName );
                }
                #endif /* ffconfigUNICODE_UTF16_SUPPORT */
                #if ( ffconfigLFN_SUPPORT != 0 )
                {
                    xLFNTotal = 0;
                }
                #endif /* ffconfigLFN_SUPPORT */
            }

            /* This function FF_FindEntryInDir( ) is either called with
            * pa_Attrib==0 or with pa_Attrib==FF_FAT_ATTR_DIR
            * In the last case the caller is looking for a directory */
            if( ( pxDirEntry->ucAttrib & pa_Attrib ) == pa_Attrib )
            {
                if( testShortname )
                {
                    /* Both strings are stored in the directory format
                     * e.g. "README  TXT", without a dot */
                    if( memcmp( src, pxFindParams->pcEntryBuffer, 11 ) == 0 )
                    {
                        pxFindParams->ulFlags |= FIND_FLAG_SHORTNAME_CHECKED | FIND_FLAG_SHORTNAME_FOUND;
                    }
                }

                #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
                    if( wcsicmp( ( const char * ) pcName, ( const char * ) pxDirEntry->pcFileName ) == 0 )
                #else
                    if( FF_stricmp( ( const char * ) pcName, ( const char * ) pxDirEntry->pcFileName ) == 0 )
                #endif /* ffconfigUNICODE_UTF16_SUPPORT */
                {
                    /* Finally get the complete information. */
                    #if ( ffconfigLFN_SUPPORT != 0 )
                        if( xLFNTotal )
                        {
                            xError = FF_PopulateLongDirent( pxIOManager, pxDirEntry, ( uint16_t ) lfnItem, &xFetchContext );

                            if( FF_isERR( xError ) )
                            {
                                break;
                            }
                        }
                        else
                    #endif /* ffconfigLFN_SUPPORT */
                    {
                        FF_PopulateShortDirent( pxIOManager, pxDirEntry, src );
                        /* HT: usCurrentItem wasn't increased here. */
                        pxDirEntry->usCurrentItem++;
                    }

                    /* Object found, the cluster number will be returned. */
                    xResult = pxDirEntry->ulObjectCluster;
                    break;
                }
            }

            #if ( ffconfigLFN_SUPPORT != 0 )
            {
                xLFNTotal = 0;
            }
            #endif
        } /* for( ; pxDirEntry->usCurrentItem < FF_MAX_ENTRIES_PER_DIRECTORY; pxDirEntry->usCurrentItem++ ) */

        {
            FF_Error_t xTempError;
            xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );

            if( FF_isERR( xError ) == pdFALSE )
            {
                xError = xTempError;
            }
        }
    } /* if( FF_isERR( xError ) == pdFALSE ) */

    if( FF_isERR( xError ) == pdFALSE )
    {
        /* If a free entry wasn't found yet, put it to the current (last) item */
        if( pxFindParams->lFreeEntry < 0 )
        {
            pxFindParams->lFreeEntry = pxDirEntry->usCurrentItem;
        }

        /* If we were checking the existence of the short-name
         * set the Checked flag now */
        if( testShortname )
        {
            pxFindParams->ulFlags |= FIND_FLAG_SHORTNAME_CHECKED;
        }
    }

    if( pxError != NULL )
    {
        *pxError = xError;
    }

    return xResult;
} /* FF_FindEntryInDir() */
/*-----------------------------------------------------------*/


/**
 *	@private
 **/
/* *INDENT-OFF* */
#if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
    uint32_t FF_FindDir( FF_IOManager_t * pxIOManager,
                         const FF_T_WCHAR * pcPath,
                         uint16_t pathLen,
                         FF_Error_t * pxError )
#else
    uint32_t FF_FindDir( FF_IOManager_t * pxIOManager,
                         const char * pcPath,
                         uint16_t pathLen,
                         FF_Error_t * pxError )
#endif
/* *INDENT-ON* */
{
    uint16_t it = 0; /* Re-entrancy Variables for FF_strtok( ). */
    BaseType_t last = pdFALSE;
    FF_DirEnt_t xMyDirectory;
    FF_FindParams_t xFindParams;
    FF_Error_t xError;
    BaseType_t xFound;

    #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
        FF_T_WCHAR mytoken[ ffconfigMAX_FILENAME ];
        FF_T_WCHAR * pcToken;
    #else
        char mytoken[ ffconfigMAX_FILENAME ];
        char * pcToken;
    #endif

    #if ( ffconfigPATH_CACHE != 0 )
        BaseType_t xIndex;
    #endif

    memset( &xFindParams, '\0', sizeof( xFindParams ) );
    xFindParams.ulDirCluster = pxIOManager->xPartition.ulRootDirCluster;

    xError = FF_ERR_NONE;

    if( pathLen <= 1 )
    {
        /* Must be the root directory.
         * 'xFindParams.ulDirCluster' will be returned containing 'pxIOManager->xPartition.ulRootDirCluster'. */
        xFound = pdTRUE;
    }
    else
    {
        /* Only the root directory '/' shall have a trailing slash in its name. */
        if( ( pcPath[ pathLen - 1 ] == '\\' ) || ( pcPath[ pathLen - 1 ] == '/' ) )
        {
            pathLen--;
        }

        xFound = pdFALSE;

        #if ( ffconfigPATH_CACHE != 0 )                   /* Is the requested pcPath in the PATH CACHE? */
        {
            FF_PendSemaphore( pxIOManager->pvSemaphore ); /* Thread safety on shared object! */
            {
                for( xIndex = 0; xIndex < ffconfigPATH_CACHE_DEPTH; xIndex++ )
                {
                    #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
                        if( wcslen( pxIOManager->xPartition.pxPathCache[ xIndex ].pcPath ) == ( size_t ) pathLen )
                    #else
                        if( strlen( pxIOManager->xPartition.pxPathCache[ xIndex ].pcPath ) == ( size_t ) pathLen )
                    #endif
                    {
                        if( FF_strmatch( pxIOManager->xPartition.pxPathCache[ xIndex ].pcPath, pcPath, pathLen ) )
                        {
                            xFindParams.ulDirCluster = pxIOManager->xPartition.pxPathCache[ xIndex ].ulDirCluster;
                            xFound = pdTRUE;
                            break;
                        }
                    }
                }
            }
            FF_ReleaseSemaphore( pxIOManager->pvSemaphore );
        }
        #endif /* ffconfigPATH_CACHE */
    }

    if( xFound == pdFALSE )
    {
        pcToken = FF_strtok( pcPath, mytoken, &it, &last, pathLen );

        do
        {
            xMyDirectory.usCurrentItem = 0;
            xFindParams.ulDirCluster = FF_FindEntryInDir( pxIOManager, &xFindParams, pcToken, ( uint8_t ) FF_FAT_ATTR_DIR, &xMyDirectory, &xError );

            if( xFindParams.ulDirCluster == 0ul )
            {
                break;
            }

            pcToken = FF_strtok( pcPath, mytoken, &it, &last, pathLen );
        } while( pcToken != NULL );

        if( ( pcToken != NULL ) &&
            ( ( FF_isERR( xError ) == pdFALSE ) || ( FF_GETERROR( xError ) == FF_ERR_DIR_END_OF_DIR ) ) )
        {
            xError = FF_createERR( FF_FINDDIR, FF_ERR_FILE_INVALID_PATH );
        }

        #if ( ffconfigPATH_CACHE != 0 ) /* Update the PATH CACHE with a new PATH. */
        {
            /* Only cache if the dir was actually found! */
            if( ( FF_isERR( xError ) == pdFALSE ) && ( xFindParams.ulDirCluster != 0ul ) )
            {
                FF_PendSemaphore( pxIOManager->pvSemaphore );
                {
                    if( pathLen < ffconfigMAX_FILENAME ) /* Ensure the PATH won't cause a buffer overrun. */
                    {
                        #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
                        {
                            memcpy( pxIOManager->xPartition.pxPathCache[ pxIOManager->xPartition.ulPCIndex ].pcPath, pcPath, pathLen * sizeof( FF_T_WCHAR ) );
                        }
                        #else
                        {
                            memcpy( pxIOManager->xPartition.pxPathCache[ pxIOManager->xPartition.ulPCIndex ].pcPath, pcPath, pathLen );
                        }
                        #endif

                        pxIOManager->xPartition.pxPathCache[ pxIOManager->xPartition.ulPCIndex ].pcPath[ pathLen ] = '\0';
                        pxIOManager->xPartition.pxPathCache[ pxIOManager->xPartition.ulPCIndex ].ulDirCluster = xFindParams.ulDirCluster;

                        pxIOManager->xPartition.ulPCIndex += 1;

                        if( pxIOManager->xPartition.ulPCIndex >= ffconfigPATH_CACHE_DEPTH )
                        {
                            pxIOManager->xPartition.ulPCIndex = 0;
                        }
                    }
                }
                FF_ReleaseSemaphore( pxIOManager->pvSemaphore );
            }
        }
        #endif /* ffconfigPATH_CACHE */
    } /* if( pathLen > 1 ) */

    if( pxError != NULL )
    {
        *pxError = xError;
    }

    return xFindParams.ulDirCluster;
} /* FF_FindDir() */
/*-----------------------------------------------------------*/


#if ( ffconfigSHORTNAME_CASE != 0 )

/**
 * *  For short-name entries, NT/XP etc store case information in byte 0x0c
 *  Use this to show proper case of "README.txt" or "source.H"
 **/
    #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
        static void FF_CaseShortName( FF_T_WCHAR * pcName,
                                      uint8_t attrib )
    #else
        static void FF_CaseShortName( char * pcName,
                                      uint8_t attrib )
    #endif
    {
        uint8_t testAttrib = FF_FAT_CASE_ATTR_BASE;

        for( ; *pcName != '\0'; pcName++ )
        {
            if( *pcName == '.' )
            {
                testAttrib = FF_FAT_CASE_ATTR_EXT;
            }
            else if( ( attrib & testAttrib ) != 0 )
            {
                if( ( *pcName >= 'A' ) && ( *pcName <= 'Z' ) )
                {
                    *pcName += 0x20;
                }
            }
            else if( ( *pcName >= 'a' ) && ( *pcName <= 'z' ) )
            {
                *pcName -= 0x20;
            }
        }
    }
#endif /* ffconfigSHORTNAME_CASE */
/*-----------------------------------------------------------*/

#if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )

/**
 * **/

    static void FF_ShortNameExpand( FF_T_WCHAR * pFileName )
    {
        int8_t * pSource = ( ( int8_t * ) pFileName ) + 13;
        int8_t * pTarget = ( ( int8_t * ) pFileName ) + 26;

        /* "aaaaaaaa.eee": 12 characters plus a zero makes 13
         * Copy from the right to the left */
        while( pTarget > ( int8_t * ) pFileName )
        {
            pTarget[ -1 ] = 0;
            pTarget[ -2 ] = *( --pSource );
            pTarget -= 2;
        }
    }
#endif /* ffconfigUNICODE_UTF16_SUPPORT */
/*-----------------------------------------------------------*/

static void FF_ProcessShortName( char * pcName )
{
    char pcShortName[ 13 ];
    char * pcTarget = pcName;
    int iSource;

    memcpy( pcShortName, pcName, 11 );

    for( iSource = 0; iSource < 11; iSource++ )
    {
        if( pcShortName[ iSource ] == 0x20 )
        {
            if( iSource >= 8 )
            {
                break;
            }

            iSource = 7;
        }
        else
        {
            if( iSource == 8 )
            {
                *( pcTarget++ ) = '.';
            }

            *( pcTarget++ ) = pcShortName[ iSource ];
        }
    }

    *pcTarget = '\0';
}
/*-----------------------------------------------------------*/


#if ( ffconfigTIME_SUPPORT != 0 )
    static void FF_PlaceTime( uint8_t * pucEntryBuffer,
                              uint32_t Offset,
                              FF_SystemTime_t * pxTime )
    {
        uint16_t myShort;

        /* HT time changes:
         * E.g. Unzip needs to use original time rather than
         * the result of FF_GetSystemTime */

        myShort = 0;
        myShort |= ( ( pxTime->Hour << 11 ) & 0xF800 );
        myShort |= ( ( pxTime->Minute << 5 ) & 0x07E0 );
        myShort |= ( ( pxTime->Second / 2 ) & 0x001F );
        FF_putShort( pucEntryBuffer, ( uint16_t ) Offset, myShort );
    }
#endif /* ffconfigTIME_SUPPORT */
/*-----------------------------------------------------------*/


#if ( ffconfigTIME_SUPPORT != 0 )
    static void FF_PlaceDate( uint8_t * pucEntryBuffer,
                              uint32_t Offset,
                              FF_SystemTime_t * pxTime )
    {
        uint16_t myShort;

        /* HT time changes:
         * Unzip needs to use original date rather than
         * the current date, so make it a parameter. */
        myShort = 0;
        myShort |= ( ( ( pxTime->Year - 1980 ) << 9 ) & 0xFE00 );
        myShort |= ( ( pxTime->Month << 5 ) & 0x01E0 );
        myShort |= ( pxTime->Day & 0x001F );
        FF_putShort( pucEntryBuffer, ( uint16_t ) Offset, myShort );
    }
#endif /*  ffconfigTIME_SUPPORT */
/*-----------------------------------------------------------*/


#if ( ffconfigTIME_SUPPORT != 0 )
    static void FF_GetTime( FF_SystemTime_t * pxTime,
                            const uint8_t * pucEntryBuffer,
                            uint32_t Offset )
    {
        uint16_t myShort;

        myShort = FF_getShort( pucEntryBuffer, ( uint16_t ) Offset );
        pxTime->Hour = ( ( ( myShort & 0xF800 ) >> 11 ) & 0x001F );
        pxTime->Minute = ( ( ( myShort & 0x07E0 ) >> 5 ) & 0x003F );
        pxTime->Second = 2 * ( myShort & 0x01F );
    }
#endif /*  ffconfigTIME_SUPPORT */
/*-----------------------------------------------------------*/


#if ( ffconfigTIME_SUPPORT != 0 )
    static void FF_GetDate( FF_SystemTime_t * pxTime,
                            const uint8_t * pucEntryBuffer,
                            uint32_t Offset )
    {
        uint16_t myShort;

        myShort = FF_getShort( pucEntryBuffer, ( uint16_t ) Offset );
        pxTime->Year = 1980 + ( ( ( myShort & 0xFE00 ) >> 9 ) & 0x07F );
        pxTime->Month = ( ( ( myShort & 0x01E0 ) >> 5 ) & 0x000F );
        pxTime->Day = myShort & 0x01F;
    }
#endif /*  ffconfigTIME_SUPPORT */
/*-----------------------------------------------------------*/

void FF_PopulateShortDirent( FF_IOManager_t * pxIOManager,
                             FF_DirEnt_t * pxDirEntry,
                             const uint8_t * pucEntryBuffer )
{
    memcpy( pxDirEntry->pcFileName, pucEntryBuffer, 11 ); /* Copy the filename into the Dirent object. */
    #if ( ffconfigLFN_SUPPORT != 0 ) && ( ffconfigINCLUDE_SHORT_NAME != 0 )
        memcpy( pxDirEntry->pcShortName, pucEntryBuffer, 11 );
        pxDirEntry->pcShortName[ 11 ] = '\0';
        FF_ProcessShortName( pxDirEntry->pcShortName );       /* For debuggers only. */
    #endif
    FF_ProcessShortName( ( char * ) pxDirEntry->pcFileName ); /* Format the shortname, for pleasant viewing. */

    #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )

        /* FileName contains a 8-bit short name
         * Expand it to a FF_T_WCHAR string */
        FF_ShortNameExpand( pxDirEntry->pcFileName );
    #endif

    ( void ) pxIOManager; /* Silence a compiler warning, about not referencing pxIOManager. */

    #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
        FF_tolower( pxDirEntry->pcFileName, ( uint32_t ) wcslen( pxDirEntry->pcFileName ) );
    #else
        FF_tolower( pxDirEntry->pcFileName, ( uint32_t ) strlen( pxDirEntry->pcFileName ) );
    #endif

    /* Get the item's Cluster address. */
    pxDirEntry->ulObjectCluster =
        ( ( uint32_t ) FF_getShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_HIGH ) << 16 ) |
        ( uint32_t ) FF_getShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_LOW );
    #if ( ffconfigTIME_SUPPORT != 0 )
        /* Get the creation Time & Date. */
        FF_GetTime( &pxDirEntry->xCreateTime, pucEntryBuffer, FF_FAT_DIRENT_CREATE_TIME );
        FF_GetDate( &pxDirEntry->xCreateTime, pucEntryBuffer, FF_FAT_DIRENT_CREATE_DATE );

        /* Get the modified Time & Date
         * HT Here xCreateTime became xModifiedTime: */
        FF_GetTime( &pxDirEntry->xModifiedTime, pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_TIME );
        FF_GetDate( &pxDirEntry->xModifiedTime, pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_DATE );
        /* Get the last accessed Date. */
        FF_GetDate( &pxDirEntry->xAccessedTime, pucEntryBuffer, FF_FAT_DIRENT_LASTACC_DATE );
        pxDirEntry->xAccessedTime.Hour = 0;
        pxDirEntry->xAccessedTime.Minute = 0;
        pxDirEntry->xAccessedTime.Second = 0;
    #endif /* if ( ffconfigTIME_SUPPORT != 0 ) */
    /* Get the filesize. */
    pxDirEntry->ulFileSize = FF_getLong( pucEntryBuffer, ( uint16_t ) ( FF_FAT_DIRENT_FILESIZE ) );
    /* Get the attribute. */
    pxDirEntry->ucAttrib = FF_getChar( pucEntryBuffer, ( uint16_t ) ( FF_FAT_DIRENT_ATTRIB ) );
}
/*-----------------------------------------------------------*/


/* Initialises a context object for FF_FetchEntryWithContext(  */
FF_Error_t FF_InitEntryFetch( FF_IOManager_t * pxIOManager,
                              uint32_t ulDirCluster,
                              FF_FetchContext_t * pxContext )
{
    FF_Error_t xError;

    memset( pxContext, 0, sizeof( FF_FetchContext_t ) );

    /* Get the total length of the chain. */
    pxContext->ulChainLength = FF_GetChainLength( pxIOManager, ulDirCluster, NULL, &xError );

    if( FF_isERR( xError ) == pdFALSE )
    {
        pxContext->ulDirCluster = ulDirCluster;
        pxContext->ulCurrentClusterLCN = ulDirCluster;

        if( pxIOManager->xPartition.ucType != FF_T_FAT32 )
        {
            /* Handle Root Dirs that don't have cluster chains! */
            if( pxContext->ulDirCluster == pxIOManager->xPartition.ulRootDirCluster )
            {
                /* This is a RootDIR, special consideration needs to be made, because it doesn't have a Cluster chain! */
                pxContext->ulChainLength = pxIOManager->xPartition.ulRootDirSectors / pxIOManager->xPartition.ulSectorsPerCluster;

                if( pxContext->ulChainLength == 0 ) /* Some media has ulRootDirSectors < ulSectorsPerCluster. This is wrong, as it should be atleast 1 cluster! */
                {
                    pxContext->ulChainLength = 1;
                }
            }
        }
    }

    return xError;
} /* FF_InitEntryFetch() */
/*-----------------------------------------------------------*/

FF_Error_t FF_CleanupEntryFetch( FF_IOManager_t * pxIOManager,
                                 FF_FetchContext_t * pxContext )
{
    FF_Error_t xError = FF_ERR_NONE;

    if( pxContext->pxBuffer )
    {
        xError = FF_ReleaseBuffer( pxIOManager, pxContext->pxBuffer );
        pxContext->pxBuffer = NULL;
    }

    return xError;
} /* FF_CleanupEntryFetch() */
/*-----------------------------------------------------------*/

/**
 *	@brief	Find the cluster for a given Entry within a directory
 *          Make an exception for the root directory ( non FAT32 only ):
 *          Just calculate the cluster ( don't consult the actual FAT )
 *
 *	@param	pxIOManager FF_IOManager_t object that was created by FF_CreateIOManager( ).
 *	@param	ulEntry     The sequence number of the entry of interest
 *  @param	pxContext   Context of current search
 *
 *	@retval	FF_ERR_NONE on success
 *	@retval	Possible error returned by FF_TraverseFAT( ) or END_OF_DIR
 *
 *  Side effects:
 *    - pxContext->ulCurrentClusterNum : relative cluster number ( 0 <= Num < ulChainLength )
 *    - pxContext->ulCurrentClusterLCN : physical cluster on the partition
 **/

static FF_Error_t FF_Traverse( FF_IOManager_t * pxIOManager,
                               uint32_t ulEntry,
                               FF_FetchContext_t * pxContext )
{
    uint32_t ulClusterNum = FF_getClusterChainNumber( pxIOManager, ulEntry, ( uint16_t ) FF_SIZEOF_DIRECTORY_ENTRY );
    FF_Error_t xError = FF_ERR_NONE;

    /* Check if we're past the last cluster ( ulChainLength is also valid for root sectors ). */
    if( ( ulClusterNum + 1 ) > pxContext->ulChainLength )
    {
        xError = FF_createERR( FF_ERR_DIR_END_OF_DIR, FF_TRAVERSE ); /* End of Dir was reached! */
    }
    else if( ( pxIOManager->xPartition.ucType != FF_T_FAT32 ) &&
             ( pxContext->ulDirCluster == pxIOManager->xPartition.ulRootDirCluster ) )
    {
        /* Double-check if the entry number isn't too high. */
        if( ulEntry > ( ( pxIOManager->xPartition.ulRootDirSectors * pxIOManager->xPartition.usBlkSize ) / FF_SIZEOF_DIRECTORY_ENTRY ) )
        {
            xError = FF_createERR( FF_ERR_DIR_END_OF_DIR, FF_FETCHENTRYWITHCONTEXT );
        }
        else
        {
            pxContext->ulCurrentClusterLCN = pxContext->ulDirCluster;
        }
    }
    else if( ulClusterNum != pxContext->ulCurrentClusterNum )
    {
        /* Traverse the fat gently! */
        if( ulClusterNum > pxContext->ulCurrentClusterNum )
        {
            /* Start traverse from the current entry. */
            pxContext->ulCurrentClusterLCN = FF_TraverseFAT( pxIOManager, pxContext->ulCurrentClusterLCN, ( ulClusterNum - pxContext->ulCurrentClusterNum ), &xError );
        }
        else
        {
            /* Start traverse from the beginning. */
            pxContext->ulCurrentClusterLCN = FF_TraverseFAT( pxIOManager, pxContext->ulDirCluster, ulClusterNum, &xError );
        }
    }

    if( FF_isERR( xError ) == pdFALSE )
    {
        pxContext->ulCurrentClusterNum = ulClusterNum;
    }

    return xError;
} /* FF_Traverse() */
/*-----------------------------------------------------------*/

FF_Error_t FF_FetchEntryWithContext( FF_IOManager_t * pxIOManager,
                                     uint32_t ulEntry,
                                     FF_FetchContext_t * pxContext,
                                     uint8_t * pEntryBuffer )
{
    uint32_t ulItemLBA;
    uint32_t ulRelItem;
    FF_Error_t xError;

    xError = FF_Traverse( pxIOManager, ulEntry, pxContext );

    if( FF_isERR( xError ) == pdFALSE )
    {
        ulRelItem = FF_getMinorBlockEntry( pxIOManager, ulEntry, ( uint32_t ) FF_SIZEOF_DIRECTORY_ENTRY );

        ulItemLBA = FF_Cluster2LBA( pxIOManager, pxContext->ulCurrentClusterLCN ) +
                    FF_getMajorBlockNumber( pxIOManager, ulEntry, ( uint32_t ) FF_SIZEOF_DIRECTORY_ENTRY );

        if( ( pxIOManager->xPartition.ucType != FF_T_FAT32 ) &&
            ( pxContext->ulDirCluster == pxIOManager->xPartition.ulRootDirCluster ) )
        {
            ulItemLBA += ( ulEntry / ( ( pxIOManager->xPartition.usBlkSize * pxIOManager->xPartition.ulSectorsPerCluster ) /
                                       FF_SIZEOF_DIRECTORY_ENTRY ) * pxIOManager->xPartition.ulSectorsPerCluster );
        }

        ulItemLBA = FF_getRealLBA( pxIOManager, ulItemLBA ) + FF_getMinorBlockNumber( pxIOManager, ulRelItem, ( uint32_t ) FF_SIZEOF_DIRECTORY_ENTRY );

        if( ( pxContext->pxBuffer == NULL ) ||
            ( pxContext->pxBuffer->ulSector != ulItemLBA ) ||
            ( ( pxContext->pxBuffer->ucMode & FF_MODE_WRITE ) != 0 ) )
        {
            if( pxContext->pxBuffer != NULL )
            {
                xError = FF_ReleaseBuffer( pxIOManager, pxContext->pxBuffer );
                pxContext->pxBuffer = NULL;
            }

            if( FF_isERR( xError ) == pdFALSE )
            {
                pxContext->pxBuffer = FF_GetBuffer( pxIOManager, ulItemLBA, FF_MODE_READ );

                if( pxContext->pxBuffer == NULL )
                {
                    xError = FF_createERR( FF_ERR_DEVICE_DRIVER_FAILED, FF_FETCHENTRYWITHCONTEXT );
                }
            }
        }

        if( ( pEntryBuffer != NULL ) && ( pxContext->pxBuffer != NULL ) )
        {
            memcpy( pEntryBuffer, pxContext->pxBuffer->pucBuffer + ( ulRelItem * FF_SIZEOF_DIRECTORY_ENTRY ), FF_SIZEOF_DIRECTORY_ENTRY );
        }
    }

    return xError;
} /* FF_FetchEntryWithContext() */
/*-----------------------------------------------------------*/


FF_Error_t FF_PushEntryWithContext( FF_IOManager_t * pxIOManager,
                                    uint32_t ulEntry,
                                    FF_FetchContext_t * pxContext,
                                    uint8_t * pEntryBuffer )
{
    uint32_t ulItemLBA;
    uint32_t ulRelItem;
    FF_Error_t xError;

    xError = FF_Traverse( pxIOManager, ulEntry, pxContext );

    if( FF_isERR( xError ) == pdFALSE )
    {
        ulRelItem = FF_getMinorBlockEntry( pxIOManager, ulEntry, ( uint32_t ) FF_SIZEOF_DIRECTORY_ENTRY );

        ulItemLBA = FF_Cluster2LBA( pxIOManager, pxContext->ulCurrentClusterLCN ) + FF_getMajorBlockNumber( pxIOManager, ulEntry, ( uint32_t ) FF_SIZEOF_DIRECTORY_ENTRY );

        if( ( pxIOManager->xPartition.ucType != FF_T_FAT32 ) &&
            ( pxContext->ulDirCluster == pxIOManager->xPartition.ulRootDirCluster ) )
        {
            ulItemLBA += ( ulEntry /
                           ( ( pxIOManager->xPartition.usBlkSize * pxIOManager->xPartition.ulSectorsPerCluster ) / FF_SIZEOF_DIRECTORY_ENTRY ) * pxIOManager->xPartition.ulSectorsPerCluster );
        }

        ulItemLBA = FF_getRealLBA( pxIOManager, ulItemLBA ) + FF_getMinorBlockNumber( pxIOManager, ulRelItem, ( uint32_t ) FF_SIZEOF_DIRECTORY_ENTRY );

        if( ( pxContext->pxBuffer == NULL ) ||
            ( pxContext->pxBuffer->ulSector != ulItemLBA ) ||
            ( ( pxContext->pxBuffer->ucMode & FF_MODE_WRITE ) == 0 ) )
        {
            if( pxContext->pxBuffer != NULL )
            {
                xError = FF_ReleaseBuffer( pxIOManager, pxContext->pxBuffer );
                pxContext->pxBuffer = NULL;
            }

            if( FF_isERR( xError ) == pdFALSE )
            {
                pxContext->pxBuffer = FF_GetBuffer( pxIOManager, ulItemLBA, FF_MODE_WRITE );

                if( pxContext->pxBuffer == NULL )
                {
                    xError = FF_createERR( FF_ERR_DEVICE_DRIVER_FAILED, FF_FETCHENTRYWITHCONTEXT );
                }
            }
        }

        /* Now change the entry: */
        if( pxContext->pxBuffer != NULL )
        {
            memcpy( pxContext->pxBuffer->pucBuffer + ( ulRelItem * FF_SIZEOF_DIRECTORY_ENTRY ), pEntryBuffer, FF_SIZEOF_DIRECTORY_ENTRY );
        }
    }

    return xError;
} /* FF_PushEntryWithContext() */
/*-----------------------------------------------------------*/


/**
 * **/
FF_Error_t FF_GetEntry( FF_IOManager_t * pxIOManager,
                        uint16_t usEntry,
                        uint32_t ulDirCluster,
                        FF_DirEnt_t * pxDirEntry )
{
/* A 32 byte directory entry. */
    uint8_t ucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ];
    FF_FetchContext_t xFetchContext;
    FF_Error_t xError;

    #if ( ffconfigLFN_SUPPORT == 0 )
        BaseType_t xLFNCount;
    #endif
    xError = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext );

    if( FF_isERR( xError ) == pdFALSE )
    {
        xError = FF_FetchEntryWithContext( pxIOManager, usEntry, &xFetchContext, ucEntryBuffer );

        if( ( FF_isERR( xError ) == pdFALSE ) &&
            ( FF_isDeleted( ucEntryBuffer ) == pdFALSE ) )
        {
            if( FF_isEndOfDir( ucEntryBuffer ) != pdFALSE )
            {
                xError = FF_createERR( FF_ERR_DIR_END_OF_DIR, FF_GETENTRY );
            }
            else
            {
                pxDirEntry->ucAttrib = FF_getChar( ucEntryBuffer, ( uint16_t ) ( FF_FAT_DIRENT_ATTRIB ) );

                if( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_LFN ) == FF_FAT_ATTR_LFN )
                {
                    #if ( ffconfigLFN_SUPPORT != 0 )
                        xError = FF_PopulateLongDirent( pxIOManager, pxDirEntry, usEntry, &xFetchContext );
                    #else
                        /* LFN Processing. */
                        xLFNCount = ( BaseType_t ) ( ucEntryBuffer[ 0 ] & ~0x40 );
                        pxDirEntry->usCurrentItem += ( xLFNCount - 1 );
                    #endif
                }
                else if( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_VOLID ) != FF_FAT_ATTR_VOLID )
                {
                    FF_PopulateShortDirent( pxIOManager, pxDirEntry, ucEntryBuffer );
                    pxDirEntry->usCurrentItem += 1;
                }
            }
        }

        {
            FF_Error_t xTempError;
            xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );

            if( FF_isERR( xError ) == pdFALSE )
            {
                xError = xTempError;
            }
        }
    }

    return xError;
} /* FF_GetEntry() */
/*-----------------------------------------------------------*/


FF_Error_t FF_PopulateLongDirent( FF_IOManager_t * pxIOManager,
                                  FF_DirEnt_t * pxDirEntry,
                                  uint16_t usEntry,
                                  FF_FetchContext_t * pxFetchContext )
{
/* First get the entire name as UTF-16 from the LFN's.
 *  Then transform into the API's native string format. */

    FF_Error_t xError;
    BaseType_t xNumLFNs;
    uint8_t ucCheckSum;
/* A 32 byte directory entry. */
    uint8_t pucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ];
    char pcShortName[ 13 ];

    #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
        UBaseType_t uiLfnLength = 0;
    #endif

    #if ( ffconfigUNICODE_UTF16_SUPPORT == 0 )
        BaseType_t xIndex, y;
    #endif

    #if ( ffconfigUNICODE_UTF16_SUPPORT == 0 ) && ( ffconfigUNICODE_UTF8_SUPPORT == 0 )
        char * pcLastPtr = pxDirEntry->pcFileName + sizeof( pxDirEntry->pcFileName );
        char * pcCurPtr;
    #endif

    #if ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
        uint16_t nLfnBegin;
        uint16_t usUtf8Len = 0;
    #endif /* ffconfigUNICODE_UTF8_SUPPORT */

    do
    {
        xError = FF_FetchEntryWithContext( pxIOManager, usEntry++, pxFetchContext, pucEntryBuffer );

        if( FF_isERR( xError ) )
        {
            /* After breaking from this do {} while ( pdFALSE ) loop, xResult will be returned. */
            break;
        }

        xNumLFNs = ( BaseType_t ) ( pucEntryBuffer[ 0 ] & ~0x40 );
        ucCheckSum = FF_getChar( pucEntryBuffer, FF_FAT_LFN_CHECKSUM );

        #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
        {
            /* UTF-16 Can simply get segments of the UTF-16 sequence
             * going forward in the directory entries ( but reversed order ). */
            while( xNumLFNs > 0 )
            {
                /* Avoid stack intensive use of a UTF-16 buffer. Stream direct to FileName dirent field in correct format. */
                /* memcpy direct! -UTF-16 support. */
                memcpy( pxDirEntry->pcFileName + ( ( xNumLFNs - 1 ) * 13 ) + 0, &( pucEntryBuffer[ FF_FAT_LFN_NAME_1 ] ), 10 );
                memcpy( pxDirEntry->pcFileName + ( ( xNumLFNs - 1 ) * 13 ) + 5, &( pucEntryBuffer[ FF_FAT_LFN_NAME_2 ] ), 12 );
                memcpy( pxDirEntry->pcFileName + ( ( xNumLFNs - 1 ) * 13 ) + 11, &( pucEntryBuffer[ FF_FAT_LFN_NAME_3 ] ), 4 );
                uiLfnLength += 13;

                xError = FF_FetchEntryWithContext( pxIOManager, usEntry++, pxFetchContext, pucEntryBuffer );

                if( FF_isERR( xError ) )
                {
                    break;
                }

                xNumLFNs--;
            }

            if( FF_isERR( xError ) )
            {
                break;
            }

            pxDirEntry->pcFileName[ uiLfnLength ] = '\0';
        }
        #endif /* ffconfigUNICODE_UTF16_SUPPORT */

        #if ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
        {
            /* UTF-8 Sequence, we can only convert this from the beginning, must receive entries in reverse. */
            nLfnBegin = usEntry - 1;

            for( xIndex = 0; xIndex < xNumLFNs; xIndex++ )
            {
                xError = FF_FetchEntryWithContext( pxIOManager, ( nLfnBegin + ( xNumLFNs - 1 ) - xIndex ), pxFetchContext, pucEntryBuffer );

                if( FF_isERR( xError ) )
                {
                    break;
                }

                /* Now have the first part of the UTF-16 sequence. Stream into a UTF-8 sequence. */
                for( y = 0; y < 5; y++ )
                {
                    xError = FF_Utf16ctoUtf8c( ( uint8_t * ) &pxDirEntry->pcFileName[ usUtf8Len ],
                                               ( uint16_t * ) &pucEntryBuffer[ FF_FAT_LFN_NAME_1 + ( y * 2 ) ], sizeof( pxDirEntry->pcFileName ) - usUtf8Len );

                    if( xError > 0 )
                    {
                        usUtf8Len += ( uint16_t ) xError;
                    }
                }

                for( y = 0; y < 6; y++ )
                {
                    xError = FF_Utf16ctoUtf8c( ( uint8_t * ) &pxDirEntry->pcFileName[ usUtf8Len ],
                                               ( uint16_t * ) &pucEntryBuffer[ FF_FAT_LFN_NAME_2 + ( y * 2 ) ], sizeof( pxDirEntry->pcFileName ) - usUtf8Len );

                    if( xError > 0 )
                    {
                        usUtf8Len += ( uint16_t ) xError;
                    }
                }

                for( y = 0; y < 2; y++ )
                {
                    xError = FF_Utf16ctoUtf8c( ( uint8_t * ) &pxDirEntry->pcFileName[ usUtf8Len ],
                                               ( uint16_t * ) &pucEntryBuffer[ FF_FAT_LFN_NAME_3 + ( y * 2 ) ], sizeof( pxDirEntry->pcFileName ) - usUtf8Len );

                    if( xError > 0 )
                    {
                        usUtf8Len += ( uint16_t ) xError;
                    }
                }

                usEntry++;
            }

            if( FF_isERR( xError ) )
            {
                break;
            }

            pxDirEntry->pcFileName[ usUtf8Len ] = '\0';

            /* Put Entry context to correct position. */
            xError = FF_FetchEntryWithContext( pxIOManager, usEntry - 1, pxFetchContext, pucEntryBuffer );

            if( FF_isERR( xError ) )
            {
                break;
            }
        }
        #endif /* ( ffconfigUNICODE_UTF8_SUPPORT != 0 ) */

        #if ( ffconfigUNICODE_UTF16_SUPPORT == 0 ) && ( ffconfigUNICODE_UTF8_SUPPORT == 0 ) /* No Unicode, simple ASCII. */
        {
            pcLastPtr[ -1 ] = '\0';
            y = xNumLFNs;

            while( xNumLFNs-- )
            {
                pcCurPtr = pxDirEntry->pcFileName + ( xNumLFNs * 13 );

                for( xIndex = 0; ( xIndex < 10 ) && ( pcCurPtr < pcLastPtr ); xIndex += 2 )
                {
                    *( pcCurPtr++ ) = ( char ) pucEntryBuffer[ FF_FAT_LFN_NAME_1 + xIndex ];
                }

                for( xIndex = 0; ( xIndex < 12 ) && ( pcCurPtr < pcLastPtr ); xIndex += 2 )
                {
                    *( pcCurPtr++ ) = ( char ) pucEntryBuffer[ FF_FAT_LFN_NAME_2 + xIndex ];
                }

                for( xIndex = 0; ( xIndex < 4 ) && ( pcCurPtr < pcLastPtr ); xIndex += 2 )
                {
                    *( pcCurPtr++ ) = ( char ) pucEntryBuffer[ FF_FAT_LFN_NAME_3 + xIndex ];
                }

                if( ( xNumLFNs == ( y - 1 ) ) && ( pcCurPtr < pcLastPtr ) )
                {
                    *pcCurPtr = '\0';
                }

                xError = FF_FetchEntryWithContext( pxIOManager, usEntry++, pxFetchContext, pucEntryBuffer );

                if( FF_isERR( xError ) )
                {
                    break;
                }
            }

            if( FF_isERR( xError ) )
            {
                break;
            }
        }
        #endif /* ( ffconfigUNICODE_UTF16_SUPPORT == 0 ) && ( ffconfigUNICODE_UTF8_SUPPORT == 0 ) */

        /* Process the Shortname. -- LFN Transformation is now complete.
         * Process the ShortName Entry. */

        /* if SHORTNAMES must be included, simple byte copy into shortname buffer. */
        #if ( ffconfigLFN_SUPPORT != 0 ) && ( ffconfigINCLUDE_SHORT_NAME != 0 )
        {
            memcpy( pxDirEntry->pcShortName, pucEntryBuffer, 11 );
            pxDirEntry->pcShortName[ 11 ] = '\0';
            FF_ProcessShortName( pxDirEntry->pcShortName );
        }
        #endif /* ( != 0 ffconfigLFN_SUPPORT ) && ( ffconfigINCLUDE_SHORT_NAME != 0 ) */

        memcpy( pcShortName, pucEntryBuffer, 11 );
        FF_ProcessShortName( pcShortName );

        if( ucCheckSum != FF_CreateChkSum( pucEntryBuffer ) )
        {
            strcpy( pxDirEntry->pcFileName, pcShortName );
            #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
            {
                FF_ShortNameExpand( pxDirEntry->pcFileName );
            }
            #endif /* ffconfigUNICODE_UTF16_SUPPORT */
        }

        /* Finally fill in the other details. */
        pxDirEntry->ulObjectCluster =
            ( ( uint32_t ) FF_getShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_HIGH ) << 16 ) |
            ( uint32_t ) FF_getShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_LOW );

        #if ( ffconfigTIME_SUPPORT != 0 )
        {
            /* Get the creation Time & Date. */
            FF_GetTime( &pxDirEntry->xCreateTime, pucEntryBuffer, FF_FAT_DIRENT_CREATE_TIME );
            FF_GetDate( &pxDirEntry->xCreateTime, pucEntryBuffer, FF_FAT_DIRENT_CREATE_DATE );
            /* Get the modified Time & Date. */
            /* HT Here xCreateTime has become xModifiedTime, as it should: */
            FF_GetTime( &pxDirEntry->xModifiedTime, pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_TIME );
            FF_GetDate( &pxDirEntry->xModifiedTime, pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_DATE );
            /* Get the last accessed Date. */
            FF_GetDate( &pxDirEntry->xAccessedTime, pucEntryBuffer, FF_FAT_DIRENT_LASTACC_DATE );
            /* HT Why should these times be zero'd ? */
            pxDirEntry->xAccessedTime.Hour = 0;
            pxDirEntry->xAccessedTime.Minute = 0;
            pxDirEntry->xAccessedTime.Second = 0;
        }
        #endif /* ffconfigTIME_SUPPORT */

        /* Get the filesize. */
        pxDirEntry->ulFileSize = FF_getLong( pucEntryBuffer, ( uint16_t ) ( FF_FAT_DIRENT_FILESIZE ) );
        /* Get the attribute. */
        pxDirEntry->ucAttrib = FF_getChar( pucEntryBuffer, ( uint16_t ) ( FF_FAT_DIRENT_ATTRIB ) );

        pxDirEntry->usCurrentItem = usEntry;
    }
    while( pdFALSE );

    return xError;
} /* FF_PopulateLongDirent() */
/*-----------------------------------------------------------*/

/**
 *	@brief	Find's the first directory entry for the provided path.
 *
 *	All values recorded in pxDirEntry must be preserved to and between calls to
 *	FF_FindNext( ).
 *
 *	If ffconfigFINDAPI_ALLOW_WILDCARDS is defined, then path will have the following behaviour:
 *
 *	path = "\"                  - Open the root dir, and iterate through all items.
 *	path = "\*.c"				- Open the root dir, showing only files matching *.c wildcard.
 *	path = "\sub1\newdir"		- Get the DIRENT for the newdir directory in /sub1/ if one exists.
 *	path = "\sub1\newdir\"		- Open the directory /sub1/newdir/ and iterate through all items.
 *	path = "\sub1\newdir\*.c"	- Open the directory /sub1/newdir/ and iterate through all items matching the *.c wildcard.
 *
 *	It is important to distinguish the differences in behaviour between opening a Find operation
 *	on a path like /sub1 and /sub1/. ( /sub1 gets the sub1 dirent from the / dir, whereas /sub/ opens the sub1 dir ).
 *
 *	Note, as compatible with other similar APIs, FreeRTOS+FAT also accepts \sub1\* for the same behaviour as
 *	/sub1/.
 *
 *	@param	pxIOManager FF_IOManager_t object that was created by FF_CreateIOManager( ).
 *	@param	pxDirEntry	FF_DirEnt_t object to store the entry information.
 *	@param	path		String to of the path to the Dir being listed.
 *
 *	@retval	0 on success
 *	@retval	FF_ERR_DEVICE_DRIVER_FAILED if device access failed.
 *	@retval -2 if Dir was not found.
 *
 **/
/* *INDENT-OFF* */
#if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
    FF_Error_t FF_FindFirst( FF_IOManager_t * pxIOManager,
                             FF_DirEnt_t * pxDirEntry,
                             const FF_T_WCHAR * pcPath )
#else
    FF_Error_t FF_FindFirst( FF_IOManager_t * pxIOManager,
                             FF_DirEnt_t * pxDirEntry,
                             const char * pcPath )
#endif
/* *INDENT-ON* */
{
    FF_Error_t xError;

    #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
        uint16_t PathLen = ( uint16_t ) wcslen( pcPath );
    #else
        uint16_t PathLen = ( uint16_t ) strlen( pcPath );
    #endif

    #if ( ffconfigFINDAPI_ALLOW_WILDCARDS != 0 )
        BaseType_t xIndex = 0;
        #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
            const FF_T_WCHAR * pcWildCard; /* Check for a Wild-card. */
        #else
            const char * pcWildCard;       /* Check for a Wild-card. */
        #endif
    #endif

    memset( pxDirEntry, 0, sizeof( FF_DirEnt_t ) );

    if( pxIOManager == NULL )
    {
        xError = FF_createERR( FF_ERR_NULL_POINTER, FF_FINDFIRST );
    }

    #if ( ffconfigREMOVABLE_MEDIA != 0 )
        else if( ( pxIOManager->ucFlags & FF_IOMAN_DEVICE_IS_EXTRACTED ) != 0 )
        {
            xError = FF_createERR( FF_ERR_IOMAN_DRIVER_NOMEDIUM, FF_FINDFIRST );
        }
    #endif /* ffconfigREMOVABLE_MEDIA */
    else
    {
        #if ( ffconfigFINDAPI_ALLOW_WILDCARDS != 0 )
        {
            pxDirEntry->pcWildCard[ 0 ] = '\0'; /* WildCard blank if its not a wildCard. */

            pcWildCard = &pcPath[ PathLen - 1 ];

            if( PathLen != 0 )
            {
                /* Open the dir of the last token. */
                while( ( *pcWildCard != '\\' ) && ( *pcWildCard != '/' ) )
                {
                    xIndex++;
                    pcWildCard--;

                    if( PathLen == xIndex )
                    {
                        break;
                    }
                }
            }

            pxDirEntry->ulDirCluster = FF_FindDir( pxIOManager, pcPath, PathLen - xIndex, &xError );

            if( FF_isERR( xError ) == pdFALSE )
            {
                if( pxDirEntry->ulDirCluster != 0 )
                {
                    /* Valid Dir found, copy the wildCard to filename! */
                    STRNCPY( pxDirEntry->pcWildCard, ++pcWildCard, ffconfigMAX_FILENAME - 1 );
                    pxDirEntry->pcWildCard[ ffconfigMAX_FILENAME - 1 ] = 0;

                    if( pxDirEntry->pcWildCard[ xIndex - 1 ] == ':' )
                    {
                        pxDirEntry->xInvertWildCard = pdTRUE;
                        pxDirEntry->pcWildCard[ xIndex - 1 ] = '\0';
                    }
                }
            }
        }
        #else /* ffconfigFINDAPI_ALLOW_WILDCARDS */
        {
            /* Get the directory cluster, if it exists. */
            pxDirEntry->ulDirCluster = FF_FindDir( pxIOManager, pcPath, PathLen, &xError );
        }
        #endif /* ffconfigFINDAPI_ALLOW_WILDCARDS */

        if( FF_isERR( xError ) == pdFALSE )
        {
            if( pxDirEntry->ulDirCluster == 0 )
            {
                xError = FF_createERR( FF_ERR_DIR_INVALID_PATH, FF_FINDFIRST );
            }
            else
            {
                /* Initialise the Fetch Context. */
                xError = FF_InitEntryFetch( pxIOManager, pxDirEntry->ulDirCluster, &( pxDirEntry->xFetchContext ) );

                if( FF_isERR( xError ) == pdFALSE )
                {
                    pxDirEntry->usCurrentItem = 0;
                    xError = FF_FindNext( pxIOManager, pxDirEntry );
                }
            }
        }
    }

    return xError;
} /* FF_FindFirst() */
/*-----------------------------------------------------------*/

/**
 *	@brief	Get's the next Entry based on the data recorded in the FF_DirEnt_t object.
 *
 *	All values recorded in pxDirEntry must be preserved to and between calls to
 *	FF_FindNext( ). Please see @see FF_FindFirst( ) for find initialisation.
 *
 *	@param	pxIOManager		FF_IOManager_t object that was created by FF_CreateIOManager( ).
 *	@param	pxDirEntry		FF_DirEnt_t object to store the entry information. ( As initialised by FF_FindFirst( )).
 *
 *	@retval FF_ERR_DEVICE_DRIVER_FAILED is device access failed.
 *
 **/
FF_Error_t FF_FindNext( FF_IOManager_t * pxIOManager,
                        FF_DirEnt_t * pxDirEntry )
{
    FF_Error_t xError;
    BaseType_t xLFNCount;
    const uint8_t * pucEntryBuffer = NULL;

    #if ( ffconfigFINDAPI_ALLOW_WILDCARDS != 0 )
        BaseType_t b;
    #endif

    if( pxIOManager == NULL )
    {
        xError = FF_createERR( FF_ERR_NULL_POINTER, FF_FINDNEXT );
    }

    #if ( ffconfigREMOVABLE_MEDIA != 0 )
        else if( ( pxIOManager->ucFlags & FF_IOMAN_DEVICE_IS_EXTRACTED ) != 0 )
        {
            xError = FF_createERR( FF_ERR_IOMAN_DRIVER_NOMEDIUM, FF_FINDNEXT );
        }
    #endif /* ffconfigREMOVABLE_MEDIA */
    else
    {
        xError = FF_ERR_NONE;

        for( ; pxDirEntry->usCurrentItem < FF_MAX_ENTRIES_PER_DIRECTORY; pxDirEntry->usCurrentItem++ )
        {
            if( ( pucEntryBuffer == NULL ) ||
                ( pucEntryBuffer >= ( pxDirEntry->xFetchContext.pxBuffer->pucBuffer + ( pxIOManager->usSectorSize - FF_SIZEOF_DIRECTORY_ENTRY ) ) ) )
            {
                xError = FF_FetchEntryWithContext( pxIOManager, pxDirEntry->usCurrentItem, &( pxDirEntry->xFetchContext ), NULL );

                if( FF_isERR( xError ) )
                {
                    break;
                }

                if( pucEntryBuffer == NULL )
                {
                    pucEntryBuffer = pxDirEntry->xFetchContext.pxBuffer->pucBuffer +
                                     ( FF_SIZEOF_DIRECTORY_ENTRY * ( pxDirEntry->usCurrentItem % ( pxIOManager->usSectorSize / FF_SIZEOF_DIRECTORY_ENTRY ) ) );
                }
                else
                {
                    pucEntryBuffer = pxDirEntry->xFetchContext.pxBuffer->pucBuffer;
                }
            }
            else
            {
                pucEntryBuffer += FF_SIZEOF_DIRECTORY_ENTRY;
            }

            if( FF_isDeleted( pucEntryBuffer ) != pdFALSE )
            {
                /* The entry is not in use or deleted. */
                continue;
            }

            if( FF_isEndOfDir( pucEntryBuffer ) )
            {
                /* End of directory, generate a pseudo error 'DIR_END_OF_DIR'. */
                xError = FF_createERR( FF_ERR_DIR_END_OF_DIR, FF_FINDNEXT );
                break;
            }

            pxDirEntry->ucAttrib = FF_getChar( pucEntryBuffer, ( uint16_t ) ( FF_FAT_DIRENT_ATTRIB ) );

            if( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_LFN ) == FF_FAT_ATTR_LFN )
            {
                /* LFN Processing. */
                xLFNCount = ( BaseType_t ) ( pucEntryBuffer[ 0 ] & ~0x40 );
                /* Get the shortname and check if it is marked deleted. */
                #if ( ffconfigLFN_SUPPORT != 0 )
                {
                    /* Reserve 32 bytes to hold one directory entry. */
                    uint8_t Buffer[ FF_SIZEOF_DIRECTORY_ENTRY ];

                    /* Fetch the shortname, and get it's checksum, or for a deleted item with
                     * orphaned LFN entries. */
                    xError = FF_FetchEntryWithContext( pxIOManager, ( uint32_t ) ( pxDirEntry->usCurrentItem + xLFNCount ), &pxDirEntry->xFetchContext, Buffer );

                    if( FF_isERR( xError ) )
                    {
                        break;
                    }

                    if( FF_isDeleted( Buffer ) == pdFALSE )
                    {
                        xError = FF_PopulateLongDirent( pxIOManager, pxDirEntry, pxDirEntry->usCurrentItem, &pxDirEntry->xFetchContext );

                        if( FF_isERR( xError ) )
                        {
                            break;
                        }

                        #if ( ffconfigINCLUDE_SHORT_NAME != 0 )
                        {
                            pxDirEntry->ucAttrib |= FF_FAT_ATTR_IS_LFN;
                        }
                        #endif

                        #if ( ffconfigFINDAPI_ALLOW_WILDCARDS != 0 )
                        {
                            #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
                                if( wcscmp( pxDirEntry->pcWildCard, L"" ) )
                            #else
                                if( pxDirEntry->pcWildCard[ 0 ] )
                            #endif
                            {
                                b = FF_wildcompare( pxDirEntry->pcWildCard, pxDirEntry->pcFileName );

                                if( pxDirEntry->xInvertWildCard != pdFALSE )
                                {
                                    b = !b;
                                }

                                if( b != 0 )
                                {
                                    break;
                                }

                                /* 'usCurrentItem' has already incremented by FF_PopulateLongDirent(),
                                 * this loop will increment it again. */
                                pxDirEntry->usCurrentItem -= 1;

                                /* xFetchContext/usCurrentItem have changed.  Update
                                 * 'pucEntryBuffer' to point to the current buffer position. */
                                pucEntryBuffer = pxDirEntry->xFetchContext.pxBuffer->pucBuffer +
                                                 ( FF_SIZEOF_DIRECTORY_ENTRY * ( pxDirEntry->usCurrentItem % ( pxIOManager->usSectorSize / FF_SIZEOF_DIRECTORY_ENTRY ) ) );
                            }
                            else
                            {
                                break;
                            }
                        }
                        #else /* ffconfigFINDAPI_ALLOW_WILDCARDS == 0 */
                        {
                            /* usCurrentItem has been incremented by FF_PopulateLongDirent().
                             * Entry will be returned. */
                            break;
                        }
                        #endif /* if ( ffconfigFINDAPI_ALLOW_WILDCARDS != 0 ) */
                    }
                }
                #else /* ffconfigLFN_SUPPORT */
                {
                    /* Increment 'usCurrentItem' with (xLFNCount-1),
                     * the loop will do an extra increment. */
                    pxDirEntry->usCurrentItem += ( xLFNCount - 1 );
                }
                #endif /* ffconfigLFN_SUPPORT */
            } /* ( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_LFN ) == FF_FAT_ATTR_LFN ) */
            else if( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_VOLID ) != FF_FAT_ATTR_VOLID )
            {
                /* If it's not a LFN entry, neither a Volume ID, it is a normal short name entry. */
                FF_PopulateShortDirent( pxIOManager, pxDirEntry, pucEntryBuffer );
                #if ( ffconfigSHORTNAME_CASE != 0 )
                {
                    /* Apply NT/XP+ bits to get correct case. */
                    FF_CaseShortName( pxDirEntry->pcFileName, FF_getChar( pucEntryBuffer, FF_FAT_CASE_OFFS ) );
                }
                #endif

                #if ( ffconfigFINDAPI_ALLOW_WILDCARDS != 0 )
                {
                    if( pxDirEntry->pcWildCard[ 0 ] )
                    {
                        b = FF_wildcompare( pxDirEntry->pcWildCard, pxDirEntry->pcFileName );

                        if( pxDirEntry->xInvertWildCard != pdFALSE )
                        {
                            b = !b;
                        }

                        if( b != 0 )
                        {
                            pxDirEntry->usCurrentItem += 1;
                            break;
                        }
                    }
                    else
                    {
                        pxDirEntry->usCurrentItem += 1;
                        break;
                    }
                }
                #else /* ffconfigFINDAPI_ALLOW_WILDCARDS */
                {
                    pxDirEntry->usCurrentItem += 1;
                    break;
                }
                #endif /* if ( ffconfigFINDAPI_ALLOW_WILDCARDS != 0 ) */
            }
        } /* for ( ; pxDirEntry->usCurrentItem < FF_MAX_ENTRIES_PER_DIRECTORY; pxDirEntry->usCurrentItem++ ) */

        if( pxDirEntry->usCurrentItem == FF_MAX_ENTRIES_PER_DIRECTORY )
        {
            xError = FF_createERR( FF_ERR_DIR_END_OF_DIR, FF_FINDNEXT );
        }

        {
            FF_Error_t xTempError;
            xTempError = FF_CleanupEntryFetch( pxIOManager, &pxDirEntry->xFetchContext );

            if( FF_isERR( xError ) == pdFALSE )
            {
                xError = xTempError;
            }
        }
    }

    return xError;
} /* FF_FindNext() */
/*-----------------------------------------------------------*/


/*
 *  Returns >= 0 for a free dirent entry.
 *  Returns <  0 with and xError code if anything goes wrong.
 */
static int32_t FF_FindFreeDirent( FF_IOManager_t * pxIOManager,
                                  FF_FindParams_t * pxFindParams,
                                  uint16_t usSequential )
{
    const uint8_t * pucEntryBuffer = NULL;
    uint16_t freeCount = 0;
    UBaseType_t uxEntry = 0;
    BaseType_t xEntryFound = pdFALSE;
    FF_Error_t xError;
    uint32_t DirLength;
    FF_FetchContext_t xFetchContext;
    uint32_t ulDirCluster = pxFindParams->ulDirCluster;

    xError = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext );

    if( FF_isERR( xError ) == pdFALSE )
    {
        uxEntry = pxFindParams->lFreeEntry >= 0 ? ( UBaseType_t ) pxFindParams->lFreeEntry : 0U;

        for( ; uxEntry < FF_MAX_ENTRIES_PER_DIRECTORY; uxEntry++ )
        {
            if( ( pucEntryBuffer == NULL ) ||
                ( pucEntryBuffer >= xFetchContext.pxBuffer->pucBuffer + ( pxIOManager->usSectorSize - FF_SIZEOF_DIRECTORY_ENTRY ) ) )
            {
                xError = FF_FetchEntryWithContext( pxIOManager, ( uint32_t ) uxEntry, &xFetchContext, NULL );

                if( FF_GETERROR( xError ) == FF_ERR_DIR_END_OF_DIR )
                {
                    xError = FF_ExtendDirectory( pxIOManager, ulDirCluster );
                    /* The value of xEntryFound will be ignored in case there was an error. */
                    xEntryFound = pdTRUE;
                    break;
                }
                else if( FF_isERR( xError ) )
                {
                    break;
                }

                if( pucEntryBuffer == NULL )
                {
                    pucEntryBuffer = xFetchContext.pxBuffer->pucBuffer +
                                     ( FF_SIZEOF_DIRECTORY_ENTRY * ( uxEntry % ( pxIOManager->usSectorSize / FF_SIZEOF_DIRECTORY_ENTRY ) ) );
                }
                else
                {
                    pucEntryBuffer = xFetchContext.pxBuffer->pucBuffer;
                }
            }
            else
            {
                /* Advance 32 bytes to point to the next directory entry. */
                pucEntryBuffer += FF_SIZEOF_DIRECTORY_ENTRY;
            }

            if( FF_isEndOfDir( pucEntryBuffer ) ) /* If its the end of the Dir, then FreeDirents from here. */
            {
                /* Check if the directory has enough space */
                DirLength = xFetchContext.ulChainLength;

                if( ( uxEntry + usSequential ) >
                    ( ( DirLength * ( ( UBaseType_t ) pxIOManager->xPartition.ulSectorsPerCluster * pxIOManager->xPartition.usBlkSize ) ) / FF_SIZEOF_DIRECTORY_ENTRY ) )
                {
                    xError = FF_ExtendDirectory( pxIOManager, ulDirCluster );
                }

                xEntryFound = pdTRUE;
                break;
            }

            if( FF_isDeleted( pucEntryBuffer ) )
            {
                if( ++freeCount == usSequential )
                {
                    xError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );
                    xEntryFound = pdTRUE;
                    uxEntry = ( uxEntry - ( usSequential - 1 ) ); /* Return the beginning entry in the sequential sequence. */
                    break;
                }
            }
            else
            {
                freeCount = 0;
            }
        } /* for ( uxEntry = 0; uxEntry < FF_MAX_ENTRIES_PER_DIRECTORY; uxEntry++ ) */

        {
            FF_Error_t xTempError;

            xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );

            if( FF_isERR( xError ) == pdFALSE )
            {
                xError = xTempError;
            }
        }
    }

    if( FF_isERR( xError ) == pdFALSE )
    {
        if( xEntryFound != pdFALSE )
        {
            /* No error has occurred and a free directory entry has been found. */
            xError = ( FF_Error_t ) uxEntry;
        }
        else
        {
            xError = FF_createERR( FF_ERR_DIR_DIRECTORY_FULL, FF_FINDFREEDIRENT );
        }
    }

    return xError;
} /* FF_FindFreeDirent() */
/*-----------------------------------------------------------*/

/* _HT_ Now FF_PutEntry has a new optional parameter *pucContents */
/* _HT_ so it can be used FF_MkDir( ) to save some code when adding . and .. entries  */
FF_Error_t FF_PutEntry( FF_IOManager_t * pxIOManager,
                        uint16_t usEntry,
                        uint32_t ulDirCluster,
                        FF_DirEnt_t * pxDirEntry,
                        uint8_t * pucContents )
{
    FF_Error_t xError;
/* Reserve 32 bytes to hold one directory entry. */
    uint8_t pucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ];
    FF_FetchContext_t xFetchContext;

    /* HT: use the standard access routine to get the same logic for root dirs. */
    xError = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext );

    if( FF_isERR( xError ) == pdFALSE )
    {
        xError = FF_FetchEntryWithContext( pxIOManager, usEntry, &xFetchContext, pucEntryBuffer );

        if( FF_isERR( xError ) == pdFALSE )
        {
            /* Cleanup probably not necessary here?
             * FF_PushEntryWithContext checks for R/W flag. */
            xError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );

            if( FF_isERR( xError ) == pdFALSE )
            {
                if( pucContents != NULL )
                {
                    memcpy( pucEntryBuffer, pucContents, sizeof( pucEntryBuffer ) );
                }

                FF_putChar( pucEntryBuffer, FF_FAT_DIRENT_ATTRIB, ( uint32_t ) pxDirEntry->ucAttrib );
                FF_putShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_HIGH, ( uint32_t ) ( pxDirEntry->ulObjectCluster >> 16 ) );
                FF_putShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_LOW, ( uint32_t ) ( pxDirEntry->ulObjectCluster ) );
                FF_putLong( pucEntryBuffer, FF_FAT_DIRENT_FILESIZE, pxDirEntry->ulFileSize );
                #if ( ffconfigTIME_SUPPORT != 0 )
                {
                    FF_GetSystemTime( &pxDirEntry->xAccessedTime );                                         /*/< Date of Last Access. */
                    FF_PlaceTime( pucEntryBuffer, FF_FAT_DIRENT_LASTACC_DATE, &pxDirEntry->xAccessedTime );
                    FF_PlaceDate( pucEntryBuffer, FF_FAT_DIRENT_LASTACC_DATE, &pxDirEntry->xAccessedTime ); /* Last accessed date. */
                    FF_PlaceTime( pucEntryBuffer, FF_FAT_DIRENT_CREATE_TIME, &pxDirEntry->xCreateTime );
                    FF_PlaceDate( pucEntryBuffer, FF_FAT_DIRENT_CREATE_DATE, &pxDirEntry->xCreateTime );
                    FF_PlaceTime( pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_TIME, &pxDirEntry->xModifiedTime );
                    FF_PlaceDate( pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_DATE, &pxDirEntry->xModifiedTime );
                }
                #endif /* ffconfigTIME_SUPPORT */
                xError = FF_PushEntryWithContext( pxIOManager, usEntry, &xFetchContext, pucEntryBuffer );
            }
        }
    }

    FF_CleanupEntryFetch( pxIOManager, &xFetchContext );

    return xError;
} /* FF_PutEntry() */
/*-----------------------------------------------------------*/

static BaseType_t FF_ValidShortChar( char cChar )
{
    return ( cChar >= 'A' && cChar <= 'Z' ) ||
           ( cChar >= 'a' && cChar <= 'z' ) || /* lower-case can be stored using NT/XP attribute. */
           ( cChar >= '0' && cChar <= '9' ) ||
           strchr( "$%-_@~`!(){}^#&", cChar ) != NULL;
} /* FF_ValidShortChar() */
/*-----------------------------------------------------------*/

/* *INDENT-OFF* */
#if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
    void FF_CreateShortName( FF_FindParams_t * pxFindParams,
                             const FF_T_WCHAR * pcLongName )
#else
    void FF_CreateShortName( FF_FindParams_t * pxFindParams,
                             const char * pcLongName )
#endif
/* *INDENT-ON* */
{
    BaseType_t xIndex, xPosition, xLastDot;
    uint16_t NameLen;

    #if ( ffconfigSHORTNAME_CASE != 0 )
        uint8_t testAttrib = FF_FAT_CASE_ATTR_BASE;
    #endif

    /* Examples:
     * "readme.TXT" will get the attribute FF_FAT_CASE_ATTR_BASE
     * "README.txt" will get the attribute FF_FAT_CASE_ATTR_EXT
     * "Readme.txt" can not be store as a short name */

    pxFindParams->ucCaseAttrib = 0; /* May get the value FF_FAT_CASE_ATTR_BASE or FF_FAT_CASE_ATTR_EXT */
    pxFindParams->ucFirstTilde = 6; /* The numerical position of the ~ */
    pxFindParams->ulFlags |= FIND_FLAG_SHORTNAME_SET | FIND_FLAG_FITS_SHORT | FIND_FLAG_SIZE_OK;

    #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
    {
        NameLen = ( uint16_t ) wcslen( pcLongName );
    }
    #else
    {
        NameLen = ( uint16_t ) strlen( pcLongName );
    }
    #endif

    /* Does pcLongName fit a shortname? */

    for( xIndex = 0, xPosition = 0, xLastDot = NameLen; xIndex < NameLen; xIndex++ )
    {
        if( pcLongName[ xIndex ] != '.' )
        {
            xPosition++;
        }
        else
        {
            xLastDot = xIndex;
        }
    }

    /* For example:
     * "FILENAME.EXT": NameLen = 12, xLastDot = 8, xPosition = 11
     * ".cproject"   : NameLen =  9, xLastDot = 0, xPosition =  8
     */

    if( ( NameLen > 12 ) ||            /* If name is longer than 12 characters (8.3). */
        ( NameLen - xPosition > 1 ) || /* If it contains more than 1 dot. */
        ( NameLen - xLastDot > 4 ) ||  /* If the file name extension is longer than 3 characters. */
        ( xLastDot > 8 ) )             /* If the file name base is too long. */
    {
        pxFindParams->ulFlags &= ~FIND_FLAG_SIZE_OK;
    }

    for( xIndex = 0, xPosition = 0; xIndex < 11; xPosition++ )
    {
        char ch = pcLongName[ xPosition ];

        if( !ch )
        {
            break;
        }

        if( ( xIndex == 0 ) && ( ch == '.' ) )
        {
            pxFindParams->ulFlags &= ~FIND_FLAG_FITS_SHORT;
            continue;
        }

        if( xPosition == xLastDot )
        {
            /* Remember where we put the first space. */
            if( pxFindParams->ucFirstTilde > xIndex )
            {
                pxFindParams->ucFirstTilde = ( uint8_t ) xIndex;
            }

            while( xIndex < 8 )
            {
                pxFindParams->pcEntryBuffer[ xIndex++ ] = 0x20;
            }

            #if ( ffconfigSHORTNAME_CASE != 0 )
            {
                testAttrib = FF_FAT_CASE_ATTR_EXT;
            }
            #endif
        }
        else
        {
            if( xIndex == 8 )
            {
                if( xPosition <= xLastDot )
                {
                    xPosition = xLastDot;
                    ch = ( int8_t ) pcLongName[ xPosition ];

                    if( ch == '\0' )
                    {
                        break;
                    }

                    ch = ( int8_t ) pcLongName[ ++xPosition ];
                    #if ( ffconfigSHORTNAME_CASE != 0 )
                    {
                        testAttrib = FF_FAT_CASE_ATTR_EXT;
                    }
                    #endif
                }
            }

            if( !FF_ValidShortChar( ch ) )
            {
                pxFindParams->ulFlags &= ~FIND_FLAG_FITS_SHORT;
                continue;
            }

            #if ( ffconfigSHORTNAME_CASE != 0 )
            {
                if( ( ch >= 'a' ) && ( ch <= 'z' ) )
                {
                    ch -= 0x20;

                    if( testAttrib )
                    {
                        pxFindParams->ucCaseAttrib |= testAttrib;
                    }
                    else
                    {
                        pxFindParams->ulFlags &= ~FIND_FLAG_FITS_SHORT; /* We had capital: does not fit. */
                    }
                }
                else if( ( ch >= 'A' ) && ( ch <= 'Z' ) )
                {
                    if( ( pxFindParams->ucCaseAttrib & testAttrib ) != 0 )
                    {
                        pxFindParams->ulFlags &= ~FIND_FLAG_FITS_SHORT; /* We had lower-case: does not fit. */
                    }

                    testAttrib = 0;
                }
            }
            #else /* if ( ffconfigSHORTNAME_CASE != 0 ) */
            {
                if( ( ch >= 'a' ) && ( ch <= 'z' ) )
                {
                    ch -= 0x20;
                }
            }
            #endif /* ffconfigSHORTNAME_CASE */
            pxFindParams->pcEntryBuffer[ xIndex++ ] = ch;
        }
    }

    if( ( xLastDot == 0 ) && ( xIndex < 6 ) )
    {
        /* This is a file name like ".info" or ".root" */
        pxFindParams->ucFirstTilde = ( uint8_t ) xIndex;
    }

    while( xIndex < 11 )
    {
        pxFindParams->pcEntryBuffer[ xIndex++ ] = 0x20;
    }

    if( ( xLastDot < pxFindParams->ucFirstTilde ) && ( xLastDot > 0 ) )
    {
        pxFindParams->ucFirstTilde = ( uint8_t ) xLastDot;
    }

    if( NameLen < pxFindParams->ucFirstTilde ) /* Names like "Abc" will become "~Abc". */
    {
        pxFindParams->ucFirstTilde = ( uint8_t ) NameLen;
    }
} /* FF_CreateShortName() */
/*-----------------------------------------------------------*/

int32_t FF_FindShortName( FF_IOManager_t * pxIOManager,
                          FF_FindParams_t * pxFindParams )
{
    char pcMyShortName[ 13 ];
    FF_DirEnt_t xMyDirectory;
    FF_Error_t xResult = 0;
    BaseType_t xIndex, x, y;
    uint16_t NameLen;
    char pcNumberBuf[ 12 ];
    uint32_t ulCluster;

    #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
        FF_T_WCHAR pcFileName[ 13 ];
    #else
        char * pcFileName = pcMyShortName;
    #endif /* ffconfigUNICODE_UTF16_SUPPORT */

    #if ( ipconfigQUICK_SHORT_FILENAME_CREATION != 0 )
        uint16_t usShortHash;
        uint32_t ulRand = 0ul;
    #endif

    memcpy( pcMyShortName, pxFindParams->pcEntryBuffer, 11 );
    FF_ProcessShortName( pcMyShortName );

    if( ( pxFindParams->ulFlags & FIND_FLAG_FITS_SHORT_OK ) == FIND_FLAG_FITS_SHORT_OK )
    {
        /* This entry will not get a LFN entry because it fits
         * perfectly into a host name */
        if( ( pxFindParams->ulFlags & FIND_FLAG_SHORTNAME_CHECKED ) != 0 )
        {
            if( ( pxFindParams->ulFlags & FIND_FLAG_SHORTNAME_FOUND ) != 0 )
            {
                xResult = FF_createERR( FF_ERR_DIR_OBJECT_EXISTS, FF_CREATESHORTNAME );
            }
            else
            {
                xResult = pxFindParams->ucCaseAttrib | 0x01;
            }
        }
        else
        {
            #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
            {
                memcpy( pcFileName, pcMyShortName, sizeof( pcMyShortName ) );
                FF_ShortNameExpand( pcFileName );
            }
            #endif
            ulCluster = FF_FindEntryInDir( pxIOManager, pxFindParams, pcFileName, 0x00, &xMyDirectory, &xResult );

            /* END_OF_DIR is not a fatal error, it only means that the entry was not found. */
            if( ( FF_isERR( xResult ) == pdFALSE ) || ( FF_GETERROR( xResult ) == FF_ERR_DIR_END_OF_DIR ) )
            {
                if( ulCluster == 0UL )
                {
                    xResult = pxFindParams->ucCaseAttrib | 0x01;
                }
                else
                {
                    xResult = FF_createERR( FF_ERR_DIR_OBJECT_EXISTS, FF_CREATESHORTNAME );
                }
            }
            else
            {
                /* There was an error, which will be returned. */
            }
        }
    }
    else
    {
        for( xIndex = ( ( pxFindParams->ulFlags & FIND_FLAG_SIZE_OK ) ? 0 : 1 ); ; xIndex++ )
        {
            if( xIndex != 0 )
            {
                #if ( ipconfigQUICK_SHORT_FILENAME_CREATION != 0 )
                {
                    /* In the first round, check if the original name can be used
                     * Makefile will be stored as "makefile" and not as "makefi~1". */

                    /* This method saves a lot of time when creating directories with
                     * many similar file names: when the short name version of a LFN already
                     * exists, try at most 3 entries with a tilde:
                     *  README~1.TXT
                     *  README~2.TXT
                     *  README~3.TXT
                     * After that create entries with pseudo-random 4-digit hex digits:
                     *  REA~E7BB.TXT
                     *  REA~BA32.TXT
                     *  REA~D394.TXT
                     */
                    if( xIndex <= 4 )
                    {
                        snprintf( pcNumberBuf, sizeof( pcNumberBuf ), "%d", ( int ) xIndex );
                    }
                    else
                    {
                        if( ulRand == 0ul )
                        {
                            ulRand = pxIOManager->xPartition.ulLastFreeCluster;
                            usShortHash = FF_GetCRC16( ( uint8_t * ) &ulRand, sizeof( ulRand ) );
                        }
                        else
                        {
                            usShortHash = FF_GetCRC16( ( uint8_t * ) &usShortHash, sizeof( usShortHash ) );
                        }

                        snprintf( pcNumberBuf, sizeof( pcNumberBuf ), "%04X", ( int ) usShortHash );
                    }
                }
                #else /* if ( ipconfigQUICK_SHORT_FILENAME_CREATION != 0 ) */
                {
                    snprintf( pcNumberBuf, sizeof( pcNumberBuf ), "%d", ( int ) xIndex );
                }
                #endif /* if ( ipconfigQUICK_SHORT_FILENAME_CREATION != 0 ) */

                NameLen = ( uint16_t ) strlen( pcNumberBuf );
                x = 7 - NameLen;

                if( x > pxFindParams->ucFirstTilde )
                {
                    x = pxFindParams->ucFirstTilde;
                }

                pxFindParams->pcEntryBuffer[ x++ ] = '~';

                for( y = 0; y < NameLen; y++ )
                {
                    pxFindParams->pcEntryBuffer[ x + y ] = pcNumberBuf[ y ];
                }
            }

            memcpy( pcMyShortName, pxFindParams->pcEntryBuffer, 11 );
            FF_ProcessShortName( pcMyShortName );

            if( FF_ShortNameExists( pxIOManager, pxFindParams->ulDirCluster, pcMyShortName, &xResult ) == pdFALSE )
            {
                break;
            }

            if( xIndex >= FF_MAX_ENTRIES_PER_DIRECTORY )
            {
                xResult = FF_createERR( FF_ERR_DIR_DIRECTORY_FULL, FF_CREATESHORTNAME );
                break;
            }
        }

        /* Add a tail and special number until we're happy :D. */
    }

    return xResult;
} /* FF_FindShortName () */
/*-----------------------------------------------------------*/


#if ( ffconfigLFN_SUPPORT != 0 )
    static int8_t FF_CreateLFNEntry( uint8_t * pucEntryBuffer,
                                     uint8_t * pcName,
                                     UBaseType_t uxNameLength,
                                     UBaseType_t uxLFN,
                                     uint8_t ucCheckSum )
    {
        /*
         *	HT for JW:
         *	Changed *pcName from 16- to of 8-bits
         *	The caller of this function doesn't need an expensive
         *	uint16_t usUtf16Name[ffconfigMAX_FILENAME + 1];
         *  in case UNICODE isn't used
         *  Also did quite a bit of optimisation here
         *  and tested well
         */
        UBaseType_t uxIndex, x;

        memset( pucEntryBuffer, 0, FF_SIZEOF_DIRECTORY_ENTRY );

        FF_putChar( pucEntryBuffer, FF_FAT_LFN_ORD, ( uint8_t ) ( ( uxLFN & ~0x40U ) ) );
        FF_putChar( pucEntryBuffer, FF_FAT_DIRENT_ATTRIB, ( uint8_t ) FF_FAT_ATTR_LFN );
        FF_putChar( pucEntryBuffer, FF_FAT_LFN_CHECKSUM, ( uint8_t ) ucCheckSum );

        /* Name_1. */
        uxIndex = 0;

        for( x = FF_FAT_LFN_NAME_1; uxIndex < 5u; uxIndex++, x += 2 )
        {
            if( uxIndex < uxNameLength )
            {
                pucEntryBuffer[ x ] = *( pcName++ );
                #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) || ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
                {
                    pucEntryBuffer[ x + 1 ] = *( pcName++ );
                }
                #endif
            }
            else if( uxIndex > uxNameLength )
            {
                pucEntryBuffer[ x ] = 0xFF;
                pucEntryBuffer[ x + 1 ] = 0xFF;
            }
        }

        /* Name_2. */
        for( x = FF_FAT_LFN_NAME_2; uxIndex < 11u; uxIndex++, x += 2 )
        {
            if( uxIndex < uxNameLength )
            {
                pucEntryBuffer[ x ] = *( pcName++ );
                #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) || ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
                {
                    pucEntryBuffer[ x + 1 ] = *( pcName++ );
                }
                #endif
            }
            else if( uxIndex > uxNameLength )
            {
                pucEntryBuffer[ x ] = 0xFF;
                pucEntryBuffer[ x + 1 ] = 0xFF;
            }
        }

        /* Name_3. */
        for( x = FF_FAT_LFN_NAME_3; uxIndex < 13u; uxIndex++, x += 2 )
        {
            if( uxIndex < uxNameLength )
            {
                pucEntryBuffer[ x ] = *( pcName++ );
                #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) || ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
                {
                    pucEntryBuffer[ x + 1 ] = *( pcName++ );
                }
                #endif
            }
            else if( uxIndex > uxNameLength )
            {
                pucEntryBuffer[ x ] = 0xFF;
                pucEntryBuffer[ x + 1 ] = 0xFF;
            }
        }

        return FF_ERR_NONE;
    } /* FF_CreateLFNEntry() */
#endif /* ffconfigLFN_SUPPORT */
/*-----------------------------------------------------------*/

#if ( ffconfigLFN_SUPPORT != 0 )
    #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
        static FF_Error_t FF_CreateLFNs( FF_IOManager_t * pxIOManager,
                                         uint32_t ulDirCluster,
                                         FF_T_WCHAR * pcName,
                                         uint8_t ucCheckSum,
                                         uint16_t usEntry )
    #else
        static FF_Error_t FF_CreateLFNs( FF_IOManager_t * pxIOManager,
                                         uint32_t ulDirCluster,
                                         char * pcName,
                                         uint8_t ucCheckSum,
                                         uint16_t usEntry )
    #endif /* if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) */
    {
        FF_Error_t xError = FF_ERR_NONE;
        BaseType_t xNumLFNs;
        BaseType_t xEndPos;
        BaseType_t xIndex;
        UBaseType_t y;
        FF_FetchContext_t xFetchContext;
        uint8_t pucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ];

        #if ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
            int32_t slRetVal;
        #endif

        #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) || ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
            uint16_t usUtf16Name[ ffconfigMAX_FILENAME + 1 ];
        #endif

        #if ( ffconfigUNICODE_UTF16_SUPPORT == 0 )
            char * NamePtr;
        #else
            int16_t * NamePtr;
        #endif


        #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
        {
            #if WCHAR_MAX <= 0xFFFF
            {
                y = wcslen( pcName );

                if( y > ffconfigMAX_FILENAME )
                {
                    xError = FF_createERR( FF_ERR_DIR_NAME_TOO_LONG, FF_CREATELFNS );
                }
                else
                {
                    wcsncpy( usUtf16Name, pcName, ffconfigMAX_FILENAME - 1 );
                    usUtf16Name[ ffconfigMAX_FILENAME - 1 ] = 0;
                }
            }
            #else /* if WCHAR_MAX <= 0xFFFF */
            {
                xIndex = 0;
                y = 0;

                while( pcName[ xIndex ] )
                {
                    FF_Utf32ctoUtf16c( &usUtf16Name[ y ], ( uint32_t ) pcName[ xIndex ], ffconfigMAX_FILENAME - xIndex );
                    y += FF_GetUtf16SequenceLen( usUtf16Name[ y ] );
                    xIndex++;

                    if( y > ffconfigMAX_FILENAME )
                    {
                        xError = FF_createERR( FF_ERR_DIR_NAME_TOO_LONG, FF_CREATELFNS );
                        break;
                    }
                }
            }
            #endif /* if WCHAR_MAX <= 0xFFFF */
        }
        #endif /* ffconfigUNICODE_UTF16_SUPPORT */

        /* Convert the name into UTF-16 format. */
        #if ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
        {
            /* Simply convert the UTF8 to UTF16 and be done with it. */
            xIndex = 0;
            y = 0;

            while( pcName[ xIndex ] != 0 )
            {
                slRetVal = FF_Utf8ctoUtf16c( &( usUtf16Name[ y ] ), ( uint8_t * ) &( pcName[ xIndex ] ), ffconfigMAX_FILENAME - xIndex );

                if( slRetVal > 0 )
                {
                    xIndex += slRetVal;
                }
                else
                {
                    break;     /* No more space in the UTF-16 buffer, simply truncate for safety. */
                }

                y += FF_GetUtf16SequenceLen( usUtf16Name[ y ] );

                if( y > ffconfigMAX_FILENAME )
                {
                    xError = FF_createERR( FF_ERR_DIR_NAME_TOO_LONG, FF_CREATELFNS );
                    break;
                }
            }
        }
        #elif ( ffconfigUNICODE_UTF16_SUPPORT == 0 )
        {
            /* Just check the length. */
            y = strlen( pcName );

            if( y > ffconfigMAX_FILENAME )
            {
                xError = FF_createERR( FF_ERR_DIR_NAME_TOO_LONG, FF_CREATELFNS );
            }
        }
        #endif /* if ( ffconfigUNICODE_UTF8_SUPPORT != 0 ) */

        /* Whole name is now in a valid UTF-16 format. Lets go make those LFN's.
         * At this point, it should a be the length of the name. */
        if( FF_isERR( xError ) == pdFALSE )
        {
            xNumLFNs = y / 13;  /* Number of LFNs is the total number of UTF-16 units, divided by 13 ( 13 units per LFN ). */
            xEndPos = y % 13;   /* The ending position in an LFN, of the last LFN UTF-16 character. */

            if( xEndPos )
            {
                xNumLFNs++;
            }
            else
            {
                xEndPos = 13;
            }

            xError = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext );

            if( FF_isERR( xError ) == pdFALSE )
            {
                #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
                {
                    NamePtr = ( int16_t * ) ( usUtf16Name + 13 * ( xNumLFNs - 1 ) );
                }
                #elif ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
                {
                    NamePtr = ( char * ) ( usUtf16Name + 13 * ( xNumLFNs - 1 ) );
                }
                #else
                {
                    NamePtr = pcName + 13 * ( xNumLFNs - 1 );
                }
                #endif /* if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) */

                /* After this point, xIndex is no longer the length of the Filename in UTF-16 units. */
                for( xIndex = xNumLFNs; xIndex > 0; xIndex-- )
                {
                    if( xIndex == xNumLFNs )
                    {
                        FF_CreateLFNEntry( pucEntryBuffer, ( uint8_t * ) NamePtr, ( UBaseType_t ) xEndPos, ( UBaseType_t ) xIndex, ucCheckSum );
                        pucEntryBuffer[ 0 ] |= 0x40;
                    }
                    else
                    {
                        FF_CreateLFNEntry( pucEntryBuffer, ( uint8_t * ) NamePtr, ( UBaseType_t ) 13u, ( UBaseType_t ) xIndex, ucCheckSum );
                    }

                    NamePtr -= 13;

                    xError = FF_PushEntryWithContext( pxIOManager, ( uint32_t ) ( usEntry + ( xNumLFNs - xIndex ) ), &xFetchContext, pucEntryBuffer );

                    if( FF_isERR( xError ) )
                    {
                        break;
                    }
                }

                {
                    FF_Error_t xTempError;

                    /* Release any buffers that were used. */
                    xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );

                    if( FF_isERR( xTempError ) == pdFALSE )
                    {
                        xError = xTempError;
                    }
                }
            }
        }

        return xError;
    }   /* FF_CreateLFNs() */
#endif /* ffconfigLFN_SUPPORT */
/*-----------------------------------------------------------*/

FF_Error_t FF_ExtendDirectory( FF_IOManager_t * pxIOManager,
                               uint32_t ulDirCluster )
{
    uint32_t xCurrentCluster;
    uint32_t xNextCluster = 0UL;
    FF_Error_t xError = FF_ERR_NONE;
    FF_FATBuffers_t xFATBuffers;

    if( ( ulDirCluster == pxIOManager->xPartition.ulRootDirCluster ) &&
        ( pxIOManager->xPartition.ucType != FF_T_FAT32 ) )
    {
        /* root directories on FAT12 and FAT16 can not be extended. */
        xError = FF_createERR( FF_ERR_DIR_CANT_EXTEND_ROOT_DIR, FF_EXTENDDIRECTORY );
    }
    else if( pxIOManager->xPartition.ulFreeClusterCount == 0UL )
    {
        /* The number of free clusters was not yet calculated or equal to zero. */
        pxIOManager->xPartition.ulFreeClusterCount = FF_CountFreeClusters( pxIOManager, &xError );
    }

    if( FF_isERR( xError ) == pdFALSE )
    {
        if( pxIOManager->xPartition.ulFreeClusterCount == 0UL )
        {
            xError = FF_createERR( FF_ERR_FAT_NO_FREE_CLUSTERS, FF_EXTENDDIRECTORY );
        }
        else
        {
            FF_LockFAT( pxIOManager );
            {
                xCurrentCluster = FF_FindEndOfChain( pxIOManager, ulDirCluster, &xError );

                if( FF_isERR( xError ) == pdFALSE )
                {
                    xNextCluster = FF_FindFreeCluster( pxIOManager, &xError, pdTRUE );

                    if( FF_isERR( xError ) == pdFALSE )
                    {
                        FF_InitFATBuffers( &xFATBuffers, FF_MODE_WRITE );

                        /* xNextCluster already has been set to 0xFFFFFFFF,
                        * now let xCurrentCluster point to xNextCluster. */

                        xError = FF_putFATEntry( pxIOManager, xCurrentCluster, xNextCluster, &xFATBuffers );
                        {
                            FF_Error_t xTempError;

                            xTempError = FF_ReleaseFATBuffers( pxIOManager, &xFATBuffers );

                            if( FF_isERR( xError ) == pdFALSE )
                            {
                                xError = xTempError;
                            }

                            xTempError = FF_DecreaseFreeClusters( pxIOManager, 1 );

                            if( FF_isERR( xError ) == pdFALSE )
                            {
                                xError = xTempError;
                            }
                        }
                    }
                }
            }
            FF_UnlockFAT( pxIOManager );

            if( FF_isERR( xError ) == pdFALSE )
            {
                /* The entire cluster will be filled with zero's,
                 * because it will contain directory data. */
                xError = FF_ClearCluster( pxIOManager, xNextCluster );
            }
        }
    }

    return xError;
} /* FF_ExtendDirectory() */
/*-----------------------------------------------------------*/

static const uint8_t forbiddenChrs[] =
{
/* Windows says: don't use these characters: '\/:*?"<>|'
 *  "     *     /	    :     <     >     ?    '\'    ?     | */
    0x22, 0x2A, 0x2F, 0x3A, 0x3C, 0x3E, 0x3F, 0x5C, 0x7F, 0x7C
};

/* *INDENT-OFF* */
#if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
    static void FF_MakeNameCompliant( FF_T_WCHAR * pcName )
#else
    static void FF_MakeNameCompliant( char * pcName )
#endif
/* *INDENT-ON* */
{
    BaseType_t index;

    if( ( uint8_t ) pcName[ 0 ] == FF_FAT_DELETED ) /* Support Japanese KANJI symbol0xE5. */
    {
        pcName[ 0 ] = 0x05;
    }

    for( ; *pcName; pcName++ )
    {
        for( index = 0; index < ( BaseType_t ) sizeof( forbiddenChrs ); index++ )
        {
            if( *pcName == forbiddenChrs[ index ] )
            {
                *pcName = '_';
                break;
            }
        }
    }
} /* FF_MakeNameCompliant() */
/*-----------------------------------------------------------*/

FF_Error_t FF_CreateDirent( FF_IOManager_t * pxIOManager,
                            FF_FindParams_t * pxFindParams,
                            FF_DirEnt_t * pxDirEntry )
{
    uint8_t pucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ];

    BaseType_t xLFNCount;
    int32_t lFreeEntry = 0L;
    FF_Error_t xReturn = FF_ERR_NONE;
    BaseType_t xEntryCount;
    FF_FetchContext_t xFetchContext;
    uint32_t ulDirCluster = pxFindParams->ulDirCluster;
    int32_t lFitShort;

    #if ( ffconfigHASH_CACHE != 0 )
        char pcShortName[ 13 ];
    #endif
    #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
        uint16_t NameLen = ( uint16_t ) wcslen( pxDirEntry->pcFileName );
    #else
        uint16_t NameLen = ( uint16_t ) strlen( pxDirEntry->pcFileName );
    #endif

    #if ( ffconfigLFN_SUPPORT != 0 )
        uint8_t ucCheckSum;
    #endif

    /* Round-up the number of LFN's needed: */
    xLFNCount = ( BaseType_t ) ( ( NameLen + 12 ) / 13 );

    #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
    {
        FF_MakeNameCompliant( pxDirEntry->pcFileName ); /* Ensure we don't break the Dir tables. */
    }
    #else
    {
        FF_MakeNameCompliant( pxDirEntry->pcFileName ); /* Ensure we don't break the Dir tables. */
    }
    #endif
    memset( pucEntryBuffer, 0, sizeof( pucEntryBuffer ) );

    #if ( ffconfigLFN_SUPPORT != 0 )
    {
        /* Create and push the LFN's. */
        /* Find enough places for the LFNs and the ShortName. */
        xEntryCount = xLFNCount + 1;
    }
    #else
    {
        xEntryCount = 1;
    }
    #endif

    /* Create the ShortName. */
    FF_LockDirectory( pxIOManager );

    do
    {
        /* Open a do {} while( pdFALSE ) loop to allow the use of break statements. */
        /* As FF_FindShortName( ) can fail, it should be called before finding a free directory entry. */
        if( ( pxFindParams->ulFlags & FIND_FLAG_SHORTNAME_SET ) == 0 )
        {
            FF_CreateShortName( pxFindParams, pxDirEntry->pcFileName );
        }

        lFitShort = FF_FindShortName( pxIOManager, pxFindParams );

        memcpy( pucEntryBuffer, pxFindParams->pcEntryBuffer, sizeof( pucEntryBuffer ) );

        if( FF_isERR( lFitShort ) )
        {
            xReturn = lFitShort;
            break;
        }

        if( lFitShort != 0 )
        {
            /* There is no need to create a LFN entry because the file name
             * fits into a normal 32-byte entry.. */
            xLFNCount = 0;
            xEntryCount = 1;
        }

        lFreeEntry = FF_FindFreeDirent( pxIOManager, pxFindParams, ( uint16_t ) xEntryCount );

        if( FF_isERR( lFreeEntry ) )
        {
            xReturn = lFreeEntry;
            break;
        }

        #if ( ffconfigLFN_SUPPORT != 0 )
        {
            if( xLFNCount > 0 )
            {
                ucCheckSum = FF_CreateChkSum( pucEntryBuffer );
                xReturn = FF_CreateLFNs( pxIOManager, ulDirCluster, pxDirEntry->pcFileName, ucCheckSum, ( uint16_t ) lFreeEntry );
            }
        }
        #else
        {
            xLFNCount = 0;
        }
        #endif /* ffconfigLFN_SUPPORT */

        if( FF_isERR( xReturn ) == pdFALSE )
        {
            #if ( ffconfigTIME_SUPPORT != 0 )
            {
                FF_GetSystemTime( &pxDirEntry->xCreateTime );        /* Date and Time Created. */
                pxDirEntry->xModifiedTime = pxDirEntry->xCreateTime; /* Date and Time Modified. */
                pxDirEntry->xAccessedTime = pxDirEntry->xCreateTime; /* Date of Last Access. */
                FF_PlaceTime( pucEntryBuffer, FF_FAT_DIRENT_CREATE_TIME, &pxDirEntry->xCreateTime );
                FF_PlaceDate( pucEntryBuffer, FF_FAT_DIRENT_CREATE_DATE, &pxDirEntry->xCreateTime );
                FF_PlaceTime( pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_TIME, &pxDirEntry->xModifiedTime );
                FF_PlaceDate( pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_DATE, &pxDirEntry->xModifiedTime );
            }
            #endif /*  ffconfigTIME_SUPPORT */

            FF_putChar( pucEntryBuffer, FF_FAT_DIRENT_ATTRIB, pxDirEntry->ucAttrib );
            #if ( ffconfigSHORTNAME_CASE != 0 )
                FF_putChar( pucEntryBuffer, FF_FAT_CASE_OFFS, ( uint32_t ) lFitShort & ( FF_FAT_CASE_ATTR_BASE | FF_FAT_CASE_ATTR_EXT ) );
            #endif
            FF_putShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_HIGH, ( uint16_t ) ( pxDirEntry->ulObjectCluster >> 16 ) );
            FF_putShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_LOW, ( uint16_t ) ( pxDirEntry->ulObjectCluster ) );
            FF_putLong( pucEntryBuffer, FF_FAT_DIRENT_FILESIZE, pxDirEntry->ulFileSize );

            xReturn = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext );

            if( FF_isERR( xReturn ) )
            {
                break;
            }

            xReturn = FF_PushEntryWithContext( pxIOManager, ( uint16_t ) ( lFreeEntry + xLFNCount ), &xFetchContext, pucEntryBuffer );

            {
                FF_Error_t xTempError;

                xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );

                if( FF_isERR( xReturn ) == pdFALSE )
                {
                    xReturn = xTempError;
                }
            }

            if( FF_isERR( xReturn ) )
            {
                break;
            }

            #if ( ffconfigHASH_CACHE != 0 )
            {
                if( FF_DirHashed( pxIOManager, ulDirCluster ) == pdFALSE )
                {
                    /* Hash the directory. */
                    FF_HashDir( pxIOManager, ulDirCluster );
                }

                memcpy( pcShortName, pucEntryBuffer, 11 );
                FF_ProcessShortName( pcShortName ); /* Format the shortname to 8.3. */
                #if ( ffconfigHASH_FUNCTION == CRC16 )
                {
                    FF_AddDirentHash( pxIOManager, ulDirCluster, ( uint32_t ) FF_GetCRC16( ( uint8_t * ) pcShortName, ( uint32_t ) strlen( pcShortName ) ) );
                }
                #elif ( ffconfigHASH_FUNCTION == CRC8 )
                {
                    FF_AddDirentHash( pxIOManager, ulDirCluster, ( uint32_t ) FF_GetCRC8( ( uint8_t * ) pcShortName, strlen( pcShortName ) ) );
                }
                #endif /* ffconfigHASH_FUNCTION */
            }
            #endif /* ffconfigHASH_CACHE*/
        }
    }
    while( pdFALSE );

    FF_UnlockDirectory( pxIOManager );

    if( FF_isERR( xReturn ) == pdFALSE )
    {
        if( pxDirEntry != NULL )
        {
            pxDirEntry->usCurrentItem = ( uint16_t ) ( lFreeEntry + xLFNCount );
        }
    }

    return xReturn;
} /* FF_CreateDirent() */
/*-----------------------------------------------------------*/

/* *INDENT-OFF* */
#if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
    uint32_t FF_CreateFile( FF_IOManager_t * pxIOManager,
                            FF_FindParams_t * pxFindParams,
                            FF_T_WCHAR * pcFileName,
                            FF_DirEnt_t * pxDirEntry,
                            FF_Error_t * pxError )
#else
    uint32_t FF_CreateFile( FF_IOManager_t * pxIOManager,
                            FF_FindParams_t * pxFindParams,
                            char * pcFileName,
                            FF_DirEnt_t * pxDirEntry,
                            FF_Error_t * pxError )
#endif /* if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) */
/* *INDENT-ON* */
{
    FF_DirEnt_t xMyFile;
    FF_Error_t xTempError, xError = FF_ERR_NONE;
    uint32_t ulResult;

    memset( &xMyFile, '\0', sizeof( xMyFile ) );

    STRNCPY( xMyFile.pcFileName, pcFileName, ffconfigMAX_FILENAME - 1 );
    xMyFile.pcFileName[ ffconfigMAX_FILENAME - 1 ] = 0;

    xMyFile.ulObjectCluster = FF_CreateClusterChain( pxIOManager, &xError );

    if( FF_isERR( xError ) == pdFALSE )
    {
        xError = FF_CreateDirent( pxIOManager, pxFindParams, &xMyFile );

        if( FF_isERR( xError ) == pdFALSE )
        {
            /* The new file now has a cluster chain and it has an entry
             * in its directory.  Copy data to a pointer provided by caller: */
            if( pxDirEntry != NULL )
            {
                memcpy( pxDirEntry, &xMyFile, sizeof( FF_DirEnt_t ) );
            }
        }
        else
        {
            /* An error occurred in FF_CreateDirent().
             * Unlink the file's cluster chain: */
            FF_LockFAT( pxIOManager );
            {
                FF_UnlinkClusterChain( pxIOManager, xMyFile.ulObjectCluster, 0 );
                xMyFile.ulObjectCluster = 0ul;
            }
            FF_UnlockFAT( pxIOManager );
        }

        /* Now flush all buffers to disk. */
        xTempError = FF_FlushCache( pxIOManager );

        if( FF_isERR( xError ) == pdFALSE )
        {
            xError = xTempError;
        }
    }

    *pxError = xError;

    if( FF_isERR( xError ) == pdFALSE )
    {
        ulResult = xMyFile.ulObjectCluster;
    }
    else
    {
        ulResult = 0;
    }

    return ulResult;
} /* FF_CreateFile() */
/*-----------------------------------------------------------*/


/**
 *	@brief Creates a Directory of the specified path.
 *
 *	@param	pxIOManager	Pointer to the FF_IOManager_t object.
 *	@param	pcPath	Path of the directory to create.
 *
 *	@retval	FF_ERR_NULL_POINTER if pxIOManager was NULL.
 *	@retval FF_ERR_DIR_OBJECT_EXISTS if the object specified by path already exists.
 *	@retval	FF_ERR_DIR_INVALID_PATH
 *	@retval FF_ERR_NONE on success.
 **/
/* *INDENT-OFF* */
#if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
    FF_Error_t FF_MkDir( FF_IOManager_t * pxIOManager,
                         const FF_T_WCHAR * pcPath )
#else
    FF_Error_t FF_MkDir( FF_IOManager_t * pxIOManager,
                         const char * pcPath )
#endif
/* *INDENT-ON* */
{
    FF_DirEnt_t xMyDirectory;

    #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
        const FF_T_WCHAR * pcDirName;
    #else
        const char * pcDirName;
    #endif
    uint8_t pucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ];
    uint32_t ulObjectCluster;
    BaseType_t xIndex;
    FF_Error_t xError = FF_ERR_NONE;

    FF_FindParams_t xFindParams;

    memset( &xFindParams, '\0', sizeof( xFindParams ) );
    /* Inform the functions that the entry will be created if not found */
    xFindParams.ulFlags |= FIND_FLAG_CREATE_FLAG;

    /* Open a do {} while ( pdFALSE ) loop */
    do
    {
        if( pxIOManager == NULL )
        {
            xError = FF_createERR( FF_ERR_NULL_POINTER, FF_MKDIR );
            break;
        }

        #if ( ffconfigREMOVABLE_MEDIA != 0 )
            if( ( pxIOManager->ucFlags & FF_IOMAN_DEVICE_IS_EXTRACTED ) != 0 )
            {
                xError = FF_createERR( FF_ERR_IOMAN_DRIVER_NOMEDIUM, FF_MKDIR );
                break;
            }
        #endif /* ffconfigREMOVABLE_MEDIA */

        #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
        {
            xIndex = ( BaseType_t ) wcslen( pcPath );
        }
        #else
        {
            xIndex = ( BaseType_t ) strlen( pcPath );
        }
        #endif

        /* Find the last slash in the path. */
        while( xIndex != 0 )
        {
            if( ( pcPath[ xIndex ] == '\\' ) || ( pcPath[ xIndex ] == '/' ) )
            {
                break;
            }

            xIndex--;
        }

        pcDirName = pcPath + xIndex + 1;

        if( xIndex == 0 )
        {
            xIndex = 1;
        }

        if( pcDirName[ 0 ] == '\0' )
        {
            xError = FF_createERR( FF_ERR_DIR_OBJECT_EXISTS, FF_MKDIR );
            break;
        }

        xFindParams.ulDirCluster = FF_FindDir( pxIOManager, pcPath, ( uint16_t ) xIndex, &xError );

        if( FF_isERR( xError ) )
        {
            break;
        }

        if( xFindParams.ulDirCluster == 0UL )
        {
            xError = FF_createERR( FF_ERR_DIR_INVALID_PATH, FF_MKDIR );
            break;
        }

        memset( &xMyDirectory, '\0', sizeof( xMyDirectory ) );

        /* Will set flags FIND_FLAG_FITS_SHORT and FIND_FLAG_SIZE_OK */
        FF_CreateShortName( &xFindParams, pcDirName );

        if( FF_FindEntryInDir( pxIOManager, &xFindParams, pcDirName, 0x00, &xMyDirectory, &xError ) )
        {
            if( FF_isERR( xError ) == pdFALSE )
            {
                xError = FF_createERR( FF_ERR_DIR_OBJECT_EXISTS, FF_MKDIR );
            }

            break;
        }

        if( ( FF_isERR( xError ) ) && ( FF_GETERROR( xError ) != FF_ERR_DIR_END_OF_DIR ) )
        {
            break;
        }

        STRNCPY( xMyDirectory.pcFileName, pcDirName, ffconfigMAX_FILENAME - 1 );
        xMyDirectory.pcFileName[ ffconfigMAX_FILENAME - 1 ] = 0;

        xMyDirectory.ulFileSize = 0;
        xMyDirectory.ucAttrib = FF_FAT_ATTR_DIR;
        xMyDirectory.ulObjectCluster = FF_CreateClusterChain( pxIOManager, &xError );

        /* Give all entries a proper time stamp, looks nicer than 1 Jan 1970 */
        #if ( ffconfigTIME_SUPPORT != 0 )
        {
            FF_GetSystemTime( &xMyDirectory.xCreateTime );
            FF_GetSystemTime( &xMyDirectory.xModifiedTime );
        }
        #endif

        if( FF_isERR( xError ) )
        {
            break;
        }

        if( xMyDirectory.ulObjectCluster == 0UL )
        {
            /* Couldn't allocate any space for the dir! */
            xError = FF_createERR( FF_ERR_DIR_EXTEND_FAILED, FF_MKDIR );
            break;
        }

        xError = FF_ClearCluster( pxIOManager, xMyDirectory.ulObjectCluster );

        if( FF_isERR( xError ) == pdFALSE )
        {
            xError = FF_CreateDirent( pxIOManager, &xFindParams, &xMyDirectory );
        }

        if( FF_isERR( xError ) )
        {
            FF_LockFAT( pxIOManager );
            {
                FF_UnlinkClusterChain( pxIOManager, xMyDirectory.ulObjectCluster, 0 );
            }
            FF_UnlockFAT( pxIOManager );
            FF_FlushCache( pxIOManager ); /* Don't override error. */
            break;
        }

        /* Write 8.3 entry "." */
        pucEntryBuffer[ 0 ] = '.';
        /* folowed by 10 spaces: */
        memset( pucEntryBuffer + 1, ' ', 10 );
        /* Clear the rest of the structure. */
        memset( pucEntryBuffer + 11, 0, FF_SIZEOF_DIRECTORY_ENTRY - 11 );

        ulObjectCluster = xMyDirectory.ulObjectCluster;
        xError = FF_PutEntry( pxIOManager, ( uint16_t ) 0u, ulObjectCluster, &xMyDirectory, pucEntryBuffer );

        if( FF_isERR( xError ) == pdFALSE )
        {
            pucEntryBuffer[ 1 ] = '.';

            if( xFindParams.ulDirCluster == pxIOManager->xPartition.ulRootDirCluster )
            {
                xMyDirectory.ulObjectCluster = 0;
            }
            else
            {
                xMyDirectory.ulObjectCluster = xFindParams.ulDirCluster;
            }

            xError = FF_PutEntry( pxIOManager, 1u, ulObjectCluster, &xMyDirectory, pucEntryBuffer );

            xMyDirectory.ulObjectCluster = ulObjectCluster;
        }

        if( FF_isERR( xError ) )
        {
            FF_LockFAT( pxIOManager );
            {
                FF_UnlinkClusterChain( pxIOManager, xMyDirectory.ulObjectCluster, 0 );
            }
            FF_UnlockFAT( pxIOManager );
        }

        FF_FlushCache( pxIOManager );
    }
    while( pdFALSE );

    return xError;
} /* FF_MkDir() */
/*-----------------------------------------------------------*/


FF_Error_t FF_RmLFNs( FF_IOManager_t * pxIOManager,
                      uint16_t usDirEntry,
                      FF_FetchContext_t * pxContext )
{
    FF_Error_t xError = FF_ERR_NONE;
    uint8_t pucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ];

    if( usDirEntry != 0 )
    {
        usDirEntry--;

        do
        {
            xError = FF_FetchEntryWithContext( pxIOManager, usDirEntry, pxContext, pucEntryBuffer );

            if( FF_isERR( xError ) )
            {
                break;
            }

            if( FF_getChar( pucEntryBuffer, ( uint16_t ) ( FF_FAT_DIRENT_ATTRIB ) ) == FF_FAT_ATTR_LFN )
            {
                FF_putChar( pucEntryBuffer, ( uint16_t ) 0, ( uint8_t ) FF_FAT_DELETED );
                xError = FF_PushEntryWithContext( pxIOManager, usDirEntry, pxContext, pucEntryBuffer );

                if( FF_isERR( xError ) )
                {
                    break;
                }
            }

            if( usDirEntry == 0 )
            {
                break;
            }

            usDirEntry--;
        } while( FF_getChar( pucEntryBuffer, ( uint16_t ) ( FF_FAT_DIRENT_ATTRIB ) ) == FF_FAT_ATTR_LFN );
    }

    return xError;
} /* FF_RmLFNs() */
/*-----------------------------------------------------------*/

#if ( ffconfigHASH_CACHE != 0 )
    FF_Error_t FF_HashDir( FF_IOManager_t * pxIOManager,
                           uint32_t ulDirCluster )
    {
        /* Find most suitable Hash Table to replace! */
        BaseType_t xIndex;
        FF_HashTable_t * pxHashCache = NULL;
        FF_FetchContext_t xFetchContext;
        const uint8_t * pucEntryBuffer = NULL;
        uint8_t ucAttrib;
        uint32_t ulHash;
        FF_Error_t xError;

        for( xIndex = 0; xIndex < ffconfigHASH_CACHE_DEPTH; xIndex++ )
        {
            if( pxIOManager->xHashCache[ xIndex ].ulNumHandles == 0 )
            {
                if( pxHashCache == NULL )
                {
                    pxHashCache = &pxIOManager->xHashCache[ xIndex ];
                }
                else
                {
                    if( ( pxIOManager->xHashCache[ xIndex ].ulMisses > pxHashCache->ulMisses ) )
                    {
                        pxHashCache = &pxIOManager->xHashCache[ xIndex ];
                    }
                }
            }
        }

        if( pxHashCache != NULL )
        {
            #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
                FF_T_WCHAR pcMyShortName[ 13 ];
            #else
                char pcMyShortName[ 13 ];
            #endif
            /* Clear the hash table! */
            memset( pxHashCache, '\0', sizeof( *pxHashCache ) );
            pxHashCache->ulDirCluster = ulDirCluster;
            pxHashCache->ulMisses = 0;

            /* Hash the directory! */

            xError = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext );

            if( FF_isERR( xError ) == pdFALSE )
            {
                for( xIndex = 0; xIndex < FF_MAX_ENTRIES_PER_DIRECTORY; xIndex++ )
                {
                    if( ( pucEntryBuffer == NULL ) ||
                        ( pucEntryBuffer >= xFetchContext.pxBuffer->pucBuffer + ( pxIOManager->usSectorSize - FF_SIZEOF_DIRECTORY_ENTRY ) ) )
                    {
                        xError = FF_FetchEntryWithContext( pxIOManager, ( uint32_t ) xIndex, &xFetchContext, NULL );

                        if( FF_isERR( xError ) )
                        {
                            break;
                        }

                        pucEntryBuffer = xFetchContext.pxBuffer->pucBuffer;
                    }
                    else
                    {
                        /* Advance the pointer 32 bytes to the next directory entry. */
                        pucEntryBuffer += FF_SIZEOF_DIRECTORY_ENTRY;
                    }

                    if( FF_isDeleted( pucEntryBuffer ) == pdFALSE )
                    {
                        ucAttrib = FF_getChar( pucEntryBuffer, FF_FAT_DIRENT_ATTRIB );

                        if( ( ( ucAttrib & FF_FAT_ATTR_LFN ) != FF_FAT_ATTR_LFN ) &&
                            ( ( ucAttrib & FF_FAT_ATTR_VOLID ) != FF_FAT_ATTR_VOLID ) )
                        {
                            memcpy( pcMyShortName, pucEntryBuffer, 11 );
                            FF_ProcessShortName( pcMyShortName );

                            if( FF_isEndOfDir( pucEntryBuffer ) )
                            {
                                break;
                            }

                            /* Generate the Hash. */
                            #if ( ffconfigHASH_FUNCTION == CRC16 )
                            {
                                ulHash = FF_GetCRC16( ( uint8_t * ) pcMyShortName, ( uint32_t ) strlen( pcMyShortName ) );
                            }
                            #else /* ffconfigHASH_FUNCTION == CRC8 */
                            {
                                ulHash = FF_GetCRC8( pcMyShortName, strlen( pcMyShortName ) );
                            }
                            #endif
                            FF_SetHash( pxHashCache, ulHash );
                        }
                    }
                } /* for( xIndex = 0; xIndex < FF_MAX_ENTRIES_PER_DIRECTORY; xIndex++ ) */

                {
                    FF_Error_t xTempError;
                    xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );

                    if( FF_isERR( xError ) == pdFALSE )
                    {
                        xError = xTempError;
                    }
                }
            }
        } /* if( pxHashCache != NULL ) */
        else
        {
            xError = -1;
        }

        return xError;
    } /* FF_HashDir() */
#endif /* ffconfigHASH_CACHE != 0 */
/*-----------------------------------------------------------*/

#if ( ffconfigHASH_CACHE != 0 )

/* FF_UnHashDir() : invalidate the hash tables of a given directory.
 * It is called when a file or sub-directory is removed or when the
 * directory itself is removed. */
    void FF_UnHashDir( FF_IOManager_t * pxIOManager,
                       uint32_t ulDirCluster )
    {
        FF_HashTable_t * pxHash = pxIOManager->xHashCache;
        FF_HashTable_t * pxLast = pxIOManager->xHashCache + ffconfigHASH_CACHE_DEPTH;

        for( ; pxHash < pxLast; pxHash++ )
        {
            if( pxHash->ulDirCluster == ulDirCluster )
            {
                pxHash->ulDirCluster = 0;
                break;
            }
        }
    } /* FF_UnHashDir() */
#endif /* ffconfigHASH_CACHE */
/*-----------------------------------------------------------*/

#if ( ffconfigHASH_CACHE != 0 )

/**
 *
 *
 **/
    void FF_SetHash( FF_HashTable_t * pxHash,
                     uint32_t ulHash )
    {
        uint32_t tblIndex = ( ulHash / 32 ) % FF_HASH_TABLE_ENTRY_COUNT;
        uint32_t tblBit = ulHash % 32;

        pxHash->ulBitTable[ tblIndex ] |= ( 0x80000000ul >> tblBit );
    } /* FF_SetHash() */
#endif /* ffconfigHASH_CACHE */
/*-----------------------------------------------------------*/

#if ( ffconfigHASH_CACHE != 0 )
    void FF_ClearHash( FF_HashTable_t * pxHash,
                       uint32_t ulHash )
    {
        if( pxHash != NULL )
        {
            uint32_t tblIndex = ( ulHash / 32 ) % FF_HASH_TABLE_ENTRY_COUNT;
            uint32_t tblBit = ulHash % 32;

            pxHash->ulBitTable[ tblIndex ] &= ~( 0x80000000ul >> tblBit );
        }
    } /* FF_ClearHash() */
#endif /* ffconfigHASH_CACHE */
/*-----------------------------------------------------------*/

#if ( ffconfigHASH_CACHE != 0 )
    BaseType_t FF_isHashSet( FF_HashTable_t * pxHash,
                             uint32_t ulHash )
    {
        FF_Error_t xResult;

        xResult = pdFALSE;

        if( pxHash != NULL )
        {
            uint32_t tblIndex = ( ulHash / 32 ) % FF_HASH_TABLE_ENTRY_COUNT;
            uint32_t tblBit = ulHash % 32;

            if( pxHash->ulBitTable[ tblIndex ] & ( 0x80000000ul >> tblBit ) )
            {
                xResult = pdTRUE;
            }
        }

        return xResult;
    } /* FF_isHashSet() */
#endif /* ffconfigHASH_CACHE */
/*-----------------------------------------------------------*/

#if ( ffconfigHASH_CACHE != 0 )
    void FF_AddDirentHash( FF_IOManager_t * pxIOManager,
                           uint32_t ulDirCluster,
                           uint32_t ulHash )
    {
        FF_HashTable_t * pxHash = pxIOManager->xHashCache;
        FF_HashTable_t * pxLast = pxIOManager->xHashCache + ffconfigHASH_CACHE_DEPTH;

        for( ; pxHash < pxLast; pxHash++ )
        {
            if( pxHash->ulDirCluster == ulDirCluster )
            {
                FF_SetHash( pxHash, ulHash );
                break;
            }
        }
    } /* FF_AddDirentHash() */
#endif /* ffconfigHASH_CACHE*/
/*-----------------------------------------------------------*/

#if ( ffconfigHASH_CACHE != 0 )
    BaseType_t FF_CheckDirentHash( FF_IOManager_t * pxIOManager,
                                   uint32_t ulDirCluster,
                                   uint32_t ulHash )
    {
        FF_HashTable_t * pxHash = pxIOManager->xHashCache;
        FF_HashTable_t * pxLast = pxIOManager->xHashCache + ffconfigHASH_CACHE_DEPTH;
        BaseType_t xResult;

        for( ; ; )
        {
            if( pxHash->ulDirCluster == ulDirCluster )
            {
                xResult = FF_isHashSet( pxHash, ulHash );
                break;
            }

            pxHash++;

            if( pxHash >= pxLast )
            {
                xResult = -1;
                break;
            }
        }

        return xResult;
    } /* FF_CheckDirentHash() */
#endif /* ffconfigHASH_CACHE */
/*-----------------------------------------------------------*/

#if ( ffconfigHASH_CACHE != 0 )
    BaseType_t FF_DirHashed( FF_IOManager_t * pxIOManager,
                             uint32_t ulDirCluster )
    {
        FF_HashTable_t * pxHash = pxIOManager->xHashCache;
        FF_HashTable_t * pxLast = pxIOManager->xHashCache + ffconfigHASH_CACHE_DEPTH;
        BaseType_t xResult;

        for( ; ; )
        {
            if( pxHash->ulDirCluster == ulDirCluster )
            {
                xResult = pdTRUE;
                break;
            }

            pxHash++;

            if( pxHash >= pxLast )
            {
                xResult = pdFALSE;
                break;
            }
        }

        return xResult;
    } /* FF_DirHashed() */
#endif /* ffconfigHASH_CACHE */
/*-----------------------------------------------------------*/
